From: Marc Horowitz Date: Mon, 22 Jul 1996 20:49:46 +0000 (+0000) Subject: this commit includes all the changes on the OV_9510_INTEGRATION and X-Git-Tag: krb5-1.0-beta7~308 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1;p=krb5.git this commit includes all the changes on the OV_9510_INTEGRATION and OV_MERGE branches. This includes, but is not limited to, the new openvision admin system, and major changes to gssapi to add functionality, and bring the implementation in line with rfc1964. before committing, the code was built and tested for netbsd and solaris. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8774 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/ChangeLog b/src/ChangeLog index fdfbd2e22..3f10ce6af 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,30 @@ +Thu Jul 18 19:12:02 1996 Marc Horowitz + + * configure.in: $krb524 works with the admin system, now + +Tue Jul 9 14:32:14 1996 Marc Horowitz + + * aclocal.m4 (USE_ANAME, USE_KDB5_LIBRARY, KRB5_LIBRARIES): change + these macros so that db (as provided in util/db2) will *always* be + used. + + * configure.in (CONFIG_DIRS): removed $kadminv4 and $krb524 for + now, since they don't work with the new admin system. this needs + to be fixed. + + * aclocal.m4 (WITH_KRB4): create new substituted variable + KRB4_INCLUDES, which is conditional on internal vs external vs no + krb4. + (USE_KADMCLNT_LIBRARY): added. + (KRB5_LIBRARIES): this macro didn't have any clue how to deal with + multiple executeables built in the same dir with different + libraries. it does now, at least for what the admin system needs. + (V5_MAKE_SHARED_LIB): this macro currently uses the LIB_SUBDIRS + make var to find the directories to build the shared library in. + This adds an optional fifth argument which is used in conjunction + with LIB_SUBDIRS for that purpose. Now, both kadm5 libraries can + be built in the same directory. + Mon Jun 17 18:34:10 1996 Tom Yu * aclocal.m4 (CHECK_DB): explicitly set $LIBS before calling @@ -97,6 +124,15 @@ Tue May 14 21:56:08 1996 Ezra Peisach * aclocal.m4 (AC_CHECK_DBM_PROTO): Arguments were not executed if using cached results. +Tue May 7 22:56:46 1996 Marc Horowitz + + * aclocal.m4 (V5_AC_OUTPUT_MAKEFILE): add a second optional + argument to specify files besides Makefile.in which should be + configureified. + * aclocal.m4 (USE_KADM_LIBRARY): removed. + * aclocal.m4 (USE_KADMSRV_LIBRARY, USE_GSSRPC_LIBRARY, + USE_GSSAPI_LIBRARY, USE_DYN_LIBRARY, USE_DB_LIBRARY): added. + Tue Apr 30 23:25:07 1996 Ken Raeburn * Makefile.in (tgz-bin, pkgdir): New targets. diff --git a/src/aclocal.m4 b/src/aclocal.m4 index 774178661..7877c585d 100644 --- a/src/aclocal.m4 +++ b/src/aclocal.m4 @@ -214,6 +214,9 @@ if test $withval = no; then DEPKRB4_LIB= KRB4_CRYPTO_LIB= DEPKRB4_CRYPTO_LIB= + KDB4_LIB= + DEPKDB4_LIB= + KRB4_INCLUDES= LDARGS= krb5_cv_build_krb4_libs=no krb5_cv_krb4_libdir= @@ -226,6 +229,9 @@ else DEPKRB4_LIB='$(TOPLIBD)/libkrb4.a' KRB4_CRYPTO_LIB='-ldes425' DEPKRB4_CRYPTO_LIB='$(TOPLIBD)/libdes425.a' + KDB4_LIB='-lkdb4' + DEPKDB4_LIB='$(TOPLIBD)/libkdb4.a' + KRB4_INCLUDES='-I$(SRCTOP)/include/kerberosIV' LDARGS= krb5_cv_build_krb4_libs=yes krb5_cv_krb4_libdir= @@ -236,11 +242,15 @@ else DEPKRB4_LIB="$withval/lib/libkrb.a" KRB4_CRYPTO_LIB='-ldes425' DEPKRB4_CRYPTO_LIB='$(TOPLIBD)/libdes425.a' + KDB4_LIB="-lkdb" + DEPKDB4_LIB="$withval/lib/libkdb.a" + KRB4_INCLUDES="-I$withval/include" LDARGS="-L$withval/lib" krb5_cv_build_krb4_libs=no krb5_cv_krb4_libdir="$withval/lib" fi fi +AC_SUBST(KRB4_INCLUDES) AC_SUBST(KRB4_LIB) AC_SUBST(KRB4_CRYPTO_LIB) AC_SUBST(DEPKRB4_LIB) @@ -475,8 +485,8 @@ dnl V5_OUTPUT_MAKEFILE dnl define(V5_AC_OUTPUT_MAKEFILE, [ifelse($1, , ac_v5_makefile_dirs=., ac_v5_makefile_dirs="$1") +ifelse($2, , filelist="", filelist="$2") dnl OPTIMIZE THIS FOR COMMON CASE!! -filelist="" for x in $ac_v5_makefile_dirs; do filelist="$filelist $x/Makefile.tmp:$ac_prepend+$x/Makefile.in+$ac_postpend" done @@ -672,13 +682,37 @@ ADD_DEF(-Dvolatile=) fi ])dnl dnl -dnl This rule tells KRB5_LIBRARIES to use the kadm library. +dnl This rule tells KRB5_LIBRARIES to use the kadm5srv library. dnl -kadm_deplib='' -kadm_lib='' -define(USE_KADM_LIBRARY,[ -kadm_deplib="\[$](TOPLIBD)/libkadm.a" -kadm_lib=-lkadm]) +kadmsrv_deplib='' +kadmsrv_lib='' +define(USE_KADMSRV_LIBRARY,[ +kadmsrv_deplib="\[$](TOPLIBD)/libkadm5srv.a" +kadmsrv_lib=-lkadm5srv]) +dnl +dnl This rule tells KRB5_LIBRARIES to use the kadm5clnt library. +dnl +kadmclnt_deplib='' +kadmclnt_lib='' +define(USE_KADMCLNT_LIBRARY,[ +kadmclnt_deplib="\[$](TOPLIBD)/libkadm5clnt.a" +kadmclnt_lib=-lkadm5clnt]) +dnl +dnl This rule tells KRB5_LIBRARIES to use the gssrpc library. +dnl +gssrpc_deplib='' +gssrpc_lib='' +define(USE_GSSRPC_LIBRARY,[ +gssrpc_deplib="\[$](TOPLIBD)/libgssrpc.a" +gssrpc_lib=-lgssrpc]) +dnl +dnl This rule tells KRB5_LIBRARIES to use the gssapi library. +dnl +gssapi_deplib='' +gssapi_lib='' +define(USE_GSSAPI_LIBRARY,[ +gssapi_deplib="\[$](TOPLIBD)/libgssapi_krb5.a" +gssapi_lib=-lgssapi_krb5]) dnl dnl This rule tells KRB5_LIBRARIES to use the krb5util library. dnl @@ -688,40 +722,20 @@ define(USE_KRB5UTIL_LIBRARY,[ kutil_deplib="\[$](TOPLIBD)/libkrb5util.a" kutil_lib=-lkrb5util]) dnl -dnl This rule tells KRB5_LIBRARIES to include the aname dbm library. +dnl This rule tells KRB5_LIBRARIES to include the aname db library. dnl -kaname_deplib='' -kaname_libs='' define(USE_ANAME,[ -WITH_ANAME_DB -kaname_libs="$dblibs" -if test "$dbval" = "db"; then - if test -n "$krb5_cv_shlib_version_libdb"; then - kaname_deplib="\$(TOPLIBD)/libdb.$krb5_cv_shlibs_ext.$krb5_cv_shlib_version_libdb" - else - kaname_deplib="\$(TOPLIBD)/libdb.$krb5_cv_noshlibs_ext" - fi -fi +USE_DB_LIBRARY ])dnl dnl -dnl This rule tells KRB5_LIBRARIES to include the kdb5 and dbm libraries. +dnl This rule tells KRB5_LIBRARIES to include the kdb5 and db libraries. dnl kdb5_deplib='' kdb5_lib='' -kdbm_deplib='' -kdbm_libs='' define(USE_KDB5_LIBRARY,[ kdb5_deplib="\[$](TOPLIBD)/libkdb5.a" kdb5_lib=-lkdb5 -WITH_KDB_DB -kdbm_libs="$dblibs" -if test "$dbval" = "db"; then - if test -n "$krb5_cv_shlib_version_libdb"; then - kdbm_deplib="\$(TOPLIBD)/libdb.$krb5_cv_shlibs_ext.$krb5_cv_shlib_version_libdb" - else - kdbm_deplib="\$(TOPLIBD)/libdb.$krb5_cv_noshlibs_ext" - fi -fi +USE_DB_LIBRARY ]) dnl dnl This rule tells KRB5_LIBRARIES to include the krb4 libraries. @@ -742,18 +756,43 @@ ss_deplib="\[$](TOPLIBD)/libss.a" ss_lib=-lss ]) dnl +dnl This rule tells KRB5_LIBRARIES to include the dyn library. +dnl +dyn_deplib='' +dyn_lib='' +define(USE_DYN_LIBRARY,[ +dyn_deplib="\[$](TOPLIBD)/libdyn.a" +dyn_lib=-ldyn +]) +dnl +dnl This rule tells KRB5_LIBRARIES to include the db library. +dnl +db_deplib='' +db_lib='' +define(USE_DB_LIBRARY,[ +db_deplib="\[$](TOPLIBD)/libdb.a" +db_lib=-ldb +]) +dnl dnl This rule generates library lists for programs. dnl define(KRB5_LIBRARIES,[ -if test ${kdbm_deplib}x = x; then -USE_ANAME -fi -DEPLIBS="\[$](DEPLOCAL_LIBRARIES) $kadm_deplib $kdb5_deplib $kutil_deplib \[$](TOPLIBD)/libkrb5.a $krb4_deplib $kdbm_deplib $kaname_deplib \[$](TOPLIBD)/libcrypto.a $ss_deplib \[$](TOPLIBD)/libcom_err.a" -LIBS="\[$](LOCAL_LIBRARIES) $kadm_lib $kdb5_lib $kutil_lib $krb4_lib -lkrb5 $kdbm_libs $kaname_libs -lcrypto $ss_lib -lcom_err $LIBS" -LDFLAGS="$LDFLAGS -L${BUILDTOP}/lib" +dnl this is ugly, but it wouldn't be necessary if krb5 didn't abuse +dnl configure so badly +SRVDEPLIBS="\[$](DEPLOCAL_LIBRARIES) $kadmsrv_deplib $gssrpc_deplib $gssapi_deplib $kdb5_deplib $kutil_deplib \[$](TOPLIBD)/libkrb5.a $kdb4_deplib $krb4_deplib \[$](TOPLIBD)/libcrypto.a $ss_deplib $dyn_deplib $db_deplib \[$](TOPLIBD)/libcom_err.a" +SRVLIBS="\[$](LOCAL_LIBRARIES) $kadmsrv_lib $gssrpc_lib $gssapi_lib $kdb5_lib $kdb4_lib $kutil_lib $krb4_lib -lkrb5 -lcrypto $ss_lib $dyn_lib $db_lib -lcom_err $LIBS" +CLNTDEPLIBS="\[$](DEPLOCAL_LIBRARIES) $kadmclnt_deplib $gssrpc_deplib $gssapi_deplib $kdb5_deplib $kutil_deplib \[$](TOPLIBD)/libkrb5.a $kdb4_deplib $krb4_deplib \[$](TOPLIBD)/libcrypto.a $ss_deplib $dyn_deplib $db_deplib \[$](TOPLIBD)/libcom_err.a" +CLNTLIBS="\[$](LOCAL_LIBRARIES) $kadmclnt_lib $gssrpc_lib $gssapi_lib $kdb5_lib $kdb4_lib $kutil_lib $krb4_lib -lkrb5 -lcrypto $ss_lib $dyn_lib $db_lib -lcom_err $LIBS" +DEPLIBS="\[$](DEPLOCAL_LIBRARIES) $kadmclnt_deplib $kadmsrv_deplib $gssrpc_deplib $gssapi_deplib $kdb5_deplib $kutil_deplib \[$](TOPLIBD)/libkrb5.a $kdb4_deplib $krb4_deplib \[$](TOPLIBD)/libcrypto.a $ss_deplib $dyn_deplib $db_deplib \[$](TOPLIBD)/libcom_err.a" +LIBS="\[$](LOCAL_LIBRARIES) $kadmclnt_lib $kadmsrv_lib $gssrpc_lib $gssapi_lib $kdb5_lib $kdb4_lib $kutil_lib $krb4_lib -lkrb5 -lcrypto $ss_lib $dyn_lib $db_lib -lcom_err $LIBS" +LDFLAGS="$LDFLAGS -L\$(TOPLIBD)" AC_SUBST(LDFLAGS) AC_SUBST(LDARGS) -AC_SUBST(DEPLIBS)]) +AC_SUBST(DEPLIBS) +AC_SUBST(SRVDEPLIBS) +AC_SUBST(SRVLIBS) +AC_SUBST(CLNTDEPLIBS) +AC_SUBST(CLNTLIBS)]) dnl dnl This rule supports the generation of the shared library object files dnl @@ -773,7 +812,8 @@ dnl dnl This rule adds the additional Makefile fragment necessary to actually dnl create the shared library dnl -dnl V5_MAKE_SHARED_LIB(libname, version, libdir, dirname_relative_to_libdir) +dnl V5_MAKE_SHARED_LIB(libname, version, libdir, dirname_relative_to_libdir, +dnl lib_subdirs) dnl define(V5_MAKE_SHARED_LIB,[ if test "[$]krb5_cv_staticlibs_enabled" = yes @@ -839,7 +879,7 @@ clean-unix:: $1.[$](SHEXT)$(VEXT): [$](LIBDONE) [$](DEPLIBS) [$](BUILDTOP)/util/makeshlib [$]@ \ "[$](SHLIB_LIBDIRS)" \ - "[$](SHLIB_LIBS)" "[$](SHLIB_LDFLAGS)" "$2" [$](LIB_SUBDIRS) + "[$](SHLIB_LIBS)" "[$](SHLIB_LDFLAGS)" "$2" [$](LIB_SUBDIRS) $5 AC_POP_MAKEFILE()dnl if test "$krb5_cv_shlibs_versioned_filenames" = "yes" ; then LinkFile($1.[$](SHEXT),$1.[$](SHEXT).$2) diff --git a/src/admin/create/ChangeLog b/src/admin/create/ChangeLog index 9fd98714a..611bdf10e 100644 --- a/src/admin/create/ChangeLog +++ b/src/admin/create/ChangeLog @@ -1,3 +1,10 @@ +Tue May 7 23:04:17 1996 Marc Horowitz + + * kdb5_create.c (add_principal): convert to used new krb5_dbe_* + tl_data functions. + + * configure.in: use USE_KADMSRV_LIBRARY instead of + USE_KADM_LIBRARY. Wed Dec 13 03:44:58 1995 Chris Provenzano (proven@mit.edu) diff --git a/src/admin/create/configure.in b/src/admin/create/configure.in index ef0252cf4..c88475027 100644 --- a/src/admin/create/configure.in +++ b/src/admin/create/configure.in @@ -1,7 +1,7 @@ AC_INIT(kdb5_create.c) CONFIG_RULES AC_PROG_INSTALL -USE_KADM_LIBRARY +USE_KADMSRV_LIBRARY USE_KDB5_LIBRARY KRB5_LIBRARIES V5_USE_SHARED_LIB diff --git a/src/admin/create/kdb5_create.c b/src/admin/create/kdb5_create.c index 2d2adeb90..963d16f03 100644 --- a/src/admin/create/kdb5_create.c +++ b/src/admin/create/kdb5_create.c @@ -454,7 +454,7 @@ add_principal(context, princ, op, pblock) krb5_error_code retval; krb5_db_entry entry; - krb5_tl_mod_princ mod_princ; + krb5_timestamp now; struct iterate_args iargs; int nentries = 1; @@ -470,10 +470,11 @@ add_principal(context, princ, op, pblock) if ((retval = krb5_copy_principal(context, princ, &entry.princ))) goto error_out; - mod_princ.mod_princ = &db_create_princ; - if ((retval = krb5_timeofday(context, &mod_princ.mod_date))) + if ((retval = krb5_timeofday(context, &now))) goto error_out; - if ((retval = krb5_dbe_encode_mod_princ_data(context, &mod_princ, &entry))) + + if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, + now, &db_create_princ))) goto error_out; switch (op) { diff --git a/src/admin/edit/ChangeLog b/src/admin/edit/ChangeLog index 12eaab25f..c01d269c1 100644 --- a/src/admin/edit/ChangeLog +++ b/src/admin/edit/ChangeLog @@ -17,6 +17,19 @@ Sat Jun 8 09:54:38 1996 Ezra Peisach * dumpv4.c (handle_one_key): Remove the temporary --with-kdb4 support. +Sun May 12 00:27:44 1996 Marc Horowitz + + * loadv4.c (enter_in_v5_db, add_principal), kdb5_edit.c + (create_db_entry, modent), dumpv4.c (dump_v4_iterator), dump.c + (dump_k5beta_iterator, process_k5beta_record): convert to use new + krb5_dbe_* tl_data functions. + + * cpw.c (enter_pwd_key): krb5_dbe_cpw() takes a kvno now. + +Tue May 7 23:16:57 1996 Marc Horowitz + + * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY + Thu May 2 22:16:01 1996 Ken Raeburn * ss_wrapper.c (main): Make sci_idx a global. This makes certain diff --git a/src/admin/edit/configure.in b/src/admin/edit/configure.in index 7567f3441..6fbf47008 100644 --- a/src/admin/edit/configure.in +++ b/src/admin/edit/configure.in @@ -5,7 +5,7 @@ AC_PROG_YACC AC_CONST AC_HEADER_STDC AC_CHECK_FUNCS(getcwd strstr) -USE_KADM_LIBRARY +USE_KADMSRV_LIBRARY USE_KDB5_LIBRARY USE_KRB4_LIBRARY USE_SS_LIBRARY diff --git a/src/admin/edit/cpw.c b/src/admin/edit/cpw.c index 1afc293e9..663fc108e 100644 --- a/src/admin/edit/cpw.c +++ b/src/admin/edit/cpw.c @@ -223,7 +223,7 @@ enter_pwd_key(cmdname, princ, ks_tuple, ks_tuple_count, entry) ks_tuple = std_ks_tuple; } if ((retval = krb5_dbe_cpw(edit_context, &master_encblock, ks_tuple, - ks_tuple_count, password, entry))) { + ks_tuple_count, password, 0, entry))) { com_err(cmdname, retval, "while storing entry for '%s'\n", princ); memset(password, 0, sizeof(password)); /* erase it */ krb5_dbe_free_contents(edit_context, entry); diff --git a/src/admin/edit/dump.c b/src/admin/edit/dump.c index 42118d555..bbda74c9d 100644 --- a/src/admin/edit/dump.c +++ b/src/admin/edit/dump.c @@ -328,7 +328,7 @@ dump_k5beta_iterator(ptr, entry) krb5_error_code retval; struct dump_args *arg; char *name, *mod_name; - krb5_tl_mod_princ *mprinc; + krb5_principal mod_princ; krb5_tl_data *pwchg; krb5_key_data *pkey, *akey, nullkey; krb5_timestamp mod_date, last_pwd_change; @@ -358,27 +358,24 @@ dump_k5beta_iterator(ptr, entry) /* * Deserialize the modifier record. */ - mprinc = (krb5_tl_mod_princ *) NULL; mod_name = (char *) NULL; + mod_princ = NULL; last_pwd_change = mod_date = 0; pkey = akey = (krb5_key_data *) NULL; - if (!(retval = krb5_dbe_decode_mod_princ_data(arg->kcontext, + if (!(retval = krb5_dbe_lookup_mod_princ_data(arg->kcontext, entry, - &mprinc))) { - if (mprinc) { - if (mprinc->mod_princ) { - /* - * Flatten the modifier name. - */ - if ((retval = krb5_unparse_name(arg->kcontext, - mprinc->mod_princ, - &mod_name))) - fprintf(stderr, mname_unp_err, arg->programname, - error_message(retval)); - krb5_free_principal(arg->kcontext, mprinc->mod_princ); - } - mod_date = mprinc->mod_date; - krb5_xfree(mprinc); + &mod_date, + &mod_princ))) { + if (mod_princ) { + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + mod_princ, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + krb5_free_principal(arg->kcontext, mod_princ); } } if (!mod_name) @@ -387,11 +384,13 @@ dump_k5beta_iterator(ptr, entry) /* * Find the last password change record and set it straight. */ - for (pwchg = entry->tl_data; - (pwchg) && (pwchg->tl_data_type != KRB5_TL_LAST_PWD_CHANGE); - pwchg = pwchg->tl_data_next); - if (pwchg) { - krb5_kdb_decode_int32(pwchg->tl_data_contents, last_pwd_change); + if (retval = + krb5_dbe_lookup_last_pwd_change(arg->kcontext, entry, + &last_pwd_change)) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + krb5_xfree(name); + return(retval); } /* @@ -823,7 +822,8 @@ find_record_end(f, fn, lineno) putc(ch, stderr); } } - + +#if 0 /* * update_tl_data() - Generate the tl_data entries. */ @@ -908,7 +908,8 @@ update_tl_data(kcontext, dbentp, mod_name, mod_date, last_pwd_change) return(kret); } - +#endif + /* * process_k5beta_record() - Handle a dump record in old format. * @@ -1129,11 +1130,15 @@ process_k5beta_record(fname, kcontext, filep, verbose, linenop) if (!(kret = krb5_parse_name(kcontext, mod_name, &mod_princ))) { - if (!(kret = update_tl_data(kcontext, - &dbent, - mod_princ, - mod_date, - last_pwd_change))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(kcontext, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(kcontext, + &dbent, + last_pwd_change))) { int one = 1; dbent.len = KRB5_KDB_V1_BASE_LENGTH; diff --git a/src/admin/edit/dumpv4.c b/src/admin/edit/dumpv4.c index 7943e1bc2..ef2eac692 100644 --- a/src/admin/edit/dumpv4.c +++ b/src/admin/edit/dumpv4.c @@ -112,7 +112,8 @@ dump_v4_iterator(ptr, entry) krb5_db_entry *entry; { struct dump_record *arg = (struct dump_record *) ptr; - krb5_tl_mod_princ *mod_princ = NULL; + krb5_principal mod_princ; + krb5_timestamp mod_time; krb5_error_code retval; int i, max_kvno, ok_key; @@ -152,12 +153,13 @@ dump_v4_iterator(ptr, entry) strcpy(principal->instance, "*"); /* Now move to mod princ */ - if (retval = krb5_dbe_decode_mod_princ_data(edit_context,entry,&mod_princ)){ + if (retval = krb5_dbe_lookup_mod_princ_data(edit_context,entry, + &mod_time, &mod_princ)){ com_err(arg->comerr_name, retval, "while unparsing db entry"); exit_status++; return retval; } - retval = krb5_524_conv_principal(edit_context, mod_princ->mod_princ, + retval = krb5_524_conv_principal(edit_context, mod_princ, principal->mod_name, principal->mod_instance, principal->mod_realm); if (retval) { @@ -228,7 +230,7 @@ found_one:; } v4_print_time(arg->f, entry->expiration); - v4_print_time(arg->f, mod_princ->mod_date); + v4_print_time(arg->f, mod_time); fprintf(arg->f, " %s %s\n", principal->mod_name, principal->mod_instance); return 0; diff --git a/src/admin/edit/kdb5_edit.c b/src/admin/edit/kdb5_edit.c index a3ac6fd06..0615e375d 100644 --- a/src/admin/edit/kdb5_edit.c +++ b/src/admin/edit/kdb5_edit.c @@ -330,7 +330,7 @@ int create_db_entry(principal, newentry) krb5_principal principal; krb5_db_entry * newentry; { - krb5_tl_mod_princ mod_princ; + krb5_timestamp now; int retval; memset(newentry, 0, sizeof(krb5_db_entry)); @@ -345,16 +345,11 @@ int create_db_entry(principal, newentry) &newentry->princ))) return retval; - if ((retval = krb5_timeofday(edit_context, &mod_princ.mod_date))) + if ((retval = krb5_timeofday(edit_context, &now))) goto create_db_entry_error; - if ((retval = krb5_copy_principal(edit_context, master_princ, - &mod_princ.mod_princ))) - goto create_db_entry_error; - - retval = krb5_dbe_encode_mod_princ_data(edit_context, &mod_princ, newentry); - krb5_xfree(mod_princ.mod_princ->data); - + retval = krb5_dbe_update_mod_princ_data(edit_context, newentry, now, + master_princ); if (!retval) return 0; @@ -1314,7 +1309,7 @@ void modent(argc, argv) char *argv[]; { krb5_db_entry entry, oldentry; - krb5_tl_mod_princ mod_princ; + krb5_timestamp now; krb5_principal kprinc; krb5_error_code retval; krb5_boolean more; @@ -1386,17 +1381,16 @@ void modent(argc, argv) free(canon); return; } - mod_princ.mod_princ = master_princ; - if ((retval = krb5_timeofday(edit_context, &mod_princ.mod_date))) { - com_err(argv[0], retval, "while fetching date"); + if ((retval = krb5_timeofday(edit_context, &now))) { + com_err(argv[0], retval, "while getting current time"); krb5_free_principal(edit_context, entry.princ); exit_status++; free(canon); return; } - if ((retval=krb5_dbe_encode_mod_princ_data(edit_context, - &mod_princ,&entry))) { - com_err(argv[0], retval, "while setting mod_prince and mod_date"); + if ((retval=krb5_dbe_update_mod_princ_data(edit_context, + &entry, now, master_princ))) { + com_err(argv[0], retval, "while setting mod_princ_data"); krb5_free_principal(edit_context, entry.princ); exit_status++; free(canon); diff --git a/src/admin/edit/loadv4.c b/src/admin/edit/loadv4.c index 78b77e06b..a1d37edc7 100644 --- a/src/admin/edit/loadv4.c +++ b/src/admin/edit/loadv4.c @@ -467,7 +467,8 @@ Principal *princ; int nentries = 1; des_cblock v4key; char *name; - krb5_tl_mod_princ mod_princ; + krb5_timestamp mod_time; + krb5_principal mod_princ; krb5_keysalt keysalt; /* don't convert local TGT if we created a TGT already.... */ @@ -518,7 +519,7 @@ Principal *princ; free(name); } - if (retval = krb5_build_principal(context, &mod_princ.mod_princ, + if (retval = krb5_build_principal(context, &mod_princ, strlen(realm), realm, princ->mod_name, princ->mod_instance[0] ? princ->mod_instance : 0, @@ -526,7 +527,7 @@ Principal *princ; krb5_free_principal(context, entry.princ); return retval; } - mod_princ.mod_date = princ->mod_date; + mod_time = princ->mod_date; entry.max_life = princ->max_life * 60 * 5; entry.max_renewable_life = rblock.max_rlife; @@ -552,7 +553,7 @@ Principal *princ; retval = krb5_dbe_create_key_data(context, &entry); if (retval) { krb5_free_principal(context, entry.princ); - krb5_free_principal(context, mod_princ.mod_princ); + krb5_free_principal(context, mod_princ); return retval; } @@ -564,10 +565,11 @@ Principal *princ; princ->key_version, &entry.key_data[0]); if (!retval) - retval = krb5_dbe_encode_mod_princ_data(context, &mod_princ, &entry); + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, mod_princ); if (retval) { krb5_db_free_principal(context, &entry, 1); - krb5_free_principal(context, mod_princ.mod_princ); + krb5_free_principal(context, mod_princ); return retval; } memset((char *)v4key, 0, sizeof(v4key)); @@ -586,7 +588,7 @@ Principal *princ; } krb5_db_free_principal(context, &entry, 1); - krb5_free_principal(context, mod_princ.mod_princ); + krb5_free_principal(context, mod_princ); return retval; } @@ -602,7 +604,8 @@ struct realm_info *pblock; krb5_error_code retval; krb5_keyblock *rkey; int nentries = 1; - krb5_tl_mod_princ mod_princ; + krb5_timestamp mod_time; + krb5_principal mod_princ; memset((char *) &entry, 0, sizeof(entry)); if (retval = krb5_copy_principal(context, princ, &entry.princ)) @@ -611,23 +614,14 @@ struct realm_info *pblock; entry.max_renewable_life = pblock->max_rlife; entry.len = KRB5_KDB_V1_BASE_LENGTH; entry.expiration = pblock->expiration; - if (retval = krb5_copy_principal(context, &db_create_princ, - &mod_princ.mod_princ)) { - krb5_free_principal(context, entry.princ); - return(retval); - } - if ((retval = krb5_timeofday(context, &mod_princ.mod_date)) || - (retval = krb5_copy_principal(context, &db_create_princ, - &mod_princ.mod_princ))) { - krb5_free_principal(context, mod_princ.mod_princ); + if ((retval = krb5_timeofday(context, &mod_time))) { krb5_db_free_principal(context, &entry, 1); return retval; } entry.attributes = pblock->flags; if (retval = krb5_dbe_create_key_data(context, &entry)) { - krb5_free_principal(context, mod_princ.mod_princ); krb5_db_free_principal(context, &entry, 1); return(retval); } @@ -639,7 +633,6 @@ struct realm_info *pblock; &master_keyblock, (krb5_keysalt *) NULL, 1, &entry.key_data[0])) { - krb5_free_principal(context, mod_princ.mod_princ); krb5_db_free_principal(context, &entry, 1); return retval; } @@ -647,7 +640,6 @@ struct realm_info *pblock; case RANDOM_KEY: if (retval = krb5_random_key(context, pblock->eblock, pblock->rseed, &rkey)) { - krb5_free_principal(context, mod_princ.mod_princ); krb5_db_free_principal(context, &entry, 1); return retval; } @@ -655,7 +647,6 @@ struct realm_info *pblock; rkey, (krb5_keysalt *) NULL, 1, &entry.key_data[0])) { - krb5_free_principal(context, mod_princ.mod_princ); krb5_db_free_principal(context, &entry, 1); return(retval); } @@ -667,11 +658,11 @@ struct realm_info *pblock; break; } - retval = krb5_dbe_encode_mod_princ_data(context, &mod_princ, &entry); + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, &db_create_princ); if (!retval) retval = krb5_db_put_principal(context, &entry, &nentries); krb5_db_free_principal(context, &entry, 1); - krb5_free_principal(context, mod_princ.mod_princ); return retval; } diff --git a/src/admin/stash/ChangeLog b/src/admin/stash/ChangeLog index e7b35b9a8..a2f6d89b4 100644 --- a/src/admin/stash/ChangeLog +++ b/src/admin/stash/ChangeLog @@ -1,3 +1,7 @@ +Sun May 12 01:16:49 1996 Marc Horowitz + + * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY + Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) * kdb5_stash.c : s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g diff --git a/src/admin/stash/configure.in b/src/admin/stash/configure.in index 93fa3eff5..1cd10cdb5 100644 --- a/src/admin/stash/configure.in +++ b/src/admin/stash/configure.in @@ -1,7 +1,7 @@ AC_INIT(kdb5_stash.c) CONFIG_RULES AC_PROG_INSTALL -USE_KADM_LIBRARY +USE_KADMSRV_LIBRARY USE_KDB5_LIBRARY KRB5_LIBRARIES V5_USE_SHARED_LIB diff --git a/src/appl/gss-sample/ChangeLog b/src/appl/gss-sample/ChangeLog index 100acd110..d82e360ef 100644 --- a/src/appl/gss-sample/ChangeLog +++ b/src/appl/gss-sample/ChangeLog @@ -4,6 +4,11 @@ Wed Jun 5 00:08:32 1996 Theodore Y. Ts'o a file, don't free free inbuf.value until after the last place where we use it. +Wed Apr 17 20:54:37 1996 Marc Horowitz + + * all files: integrated changes from OpenVision as of + October 1995 + Tue Mar 12 23:46:26 1996 Ken Raeburn * gss-server.c (timeval_subtract): Use old-style function diff --git a/src/appl/gss-sample/README b/src/appl/gss-sample/README index a8d0afa7e..52b1b2143 100644 --- a/src/appl/gss-sample/README +++ b/src/appl/gss-sample/README @@ -41,26 +41,30 @@ interfaces. The server's command line usage is - gss-server [-port port] [-v2] service_name + gss-server [-port port] [-k keytab] service_name where service_name is a GSS-API service name of the form -"service@host". The server will accept TCP connections on port -(default 4444) and establish contexts as service_name. The -v2 option -means that the GSSAPI v2 calls should be used (and tested). - +"service@host" (or just "service", in which case the local host name +is used). The server will accept TCP connections on port (default +4444) and establish contexts as service_name. If you compile with +GSS_KRB5 defined and link against the MIT Kerberos libraries, the -k +option specifies a keytab to use instead of the default one. The client's command line usage is - gss-client [-port port] [-v2] [-d] host service_name msg + gss-client [-port port] [-d] host service_name msg where host is the host running the server, service_name is the service -name that the server will establish connections as, and msg is the -message. The client connects to the TCP on (default 4444) -and performs the exchange. The "-d" option specifies delegation - -a forwardable TGT will be sent to the server, which will put it in -its credential cache (you must kinit -f for this to work). -The -v2 option means that the GSSAPI v2 calls should be used (and -tested). +name that the server will establish connections as (if you don't +specify the host name in the service name when running gss-server, and +it's running on a different machine from gss-client, make sure to +specify the server's host name in the service name you specify to +gss-client!) and msg is the message. The client connects to the TCP +on (default 4444) and performs a context +establishment. The "-d" option specifies delegation - a forwardable +TGT will be sent to the server, which will put it in its credential +cache (you must kinit -f for this to work). The -v2 option means that +the GSSAPI v2 calls should be used (and tested). If you are using this sample application with OpenVision's Kerberos 5 GSS-API mechanism: @@ -69,9 +73,10 @@ GSS-API mechanism: -lisode -lcom_err. 2. Make sure that the principal corresponding to service_name is in -the default keytab on the server host, and that the gss-server process -can read the keytab. For example, the service name "host@server" -corresponds to the Kerberos principal "host/server.domain.com@REALM". +the default or specified keytab on the server host, and that the +gss-server process can read the keytab. For example, the service name +"host@server" corresponds to the Kerberos principal +"host/server.domain.com@REALM". This sample application uses the following GSS-API functions: @@ -85,3 +90,5 @@ This sample application uses the following GSS-API functions: Barry Jaspan, bjaspan@security.ov.com OpenVision Technologies, Inc. + +$Id$ diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c index ff1bfdda1..fca0ef231 100644 --- a/src/appl/gss-sample/gss-client.c +++ b/src/appl/gss-sample/gss-client.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + #include #include #include @@ -31,337 +35,16 @@ #include #include -#include #include - -#ifdef USE_STRING_H -#include -#else -#include -#endif - -int establish_context(); -int connect_to_server(); -int call_server(); -int client_establish_context(); - -int send_token(); -int recv_token(); -void read_file(); - -int deleg_flag; -void display_status(); - -extern FILE *display_file; - +#include "gss-misc.h" void usage() { - fprintf(stderr, "Usage: gss-client [-port port] [-d] [-v2] host service \ + fprintf(stderr, "Usage: gss-client [-port port] [-d] host service \ msg\n"); exit(1); } -int main(argc, argv) - int argc; - char **argv; -{ - char *service_name, *server_host, *msg; - u_short port = 4444; - int v2 = 0; - int use_file = 0; - - display_file = stdout; - deleg_flag = 0; - - /* Parse arguments. */ - argc--; argv++; - while (argc) { - if (strcmp(*argv, "-port") == 0) { - argc--; argv++; - if (!argc) usage(); - port = atoi(*argv); - } else if (strcmp(*argv, "-v2") == 0) { - v2 = 1; - } else if (strcmp(*argv, "-d") == 0) { - deleg_flag = GSS_C_DELEG_FLAG; - } else if (strcmp(*argv, "-f") == 0) { - use_file = 1; - } else - break; - argc--; argv++; - } - if (argc != 3) - usage(); - - server_host = *argv++; - service_name = *argv++; - msg = *argv++; - - if (call_server(server_host, port, v2, service_name, msg, use_file) < 0) - exit(1); - - return 0; -} - -/* - * Function: call_server - * - * Purpose: Call the "sign" service. - * - * Arguments: - * - * host (r) the host providing the service - * port (r) the port to connect to on host - * service_name (r) the GSS-API service name to authenticate to - * msg (r) the message to have "signed" - * - * Returns: 0 on success, -1 on failure - * - * Effects: - * - * call_server opens a TCP connection to and establishes a - * GSS-API context with service_name over the connection. It then - * seals msg in a GSS-API token with gss_seal, sends it to the server, - * reads back a GSS-API signature block for msg from the server, and - * verifies it with gss_verify. -1 is returned if any step fails, - * otherwise 0 is returned. - */ -int call_server(host, port, dov2, service_name, msg, use_file) - char *host; - u_short port; - int dov2; - char *service_name; - char *msg; - int use_file; -{ - gss_ctx_id_t context; - gss_buffer_desc in_buf, out_buf, context_token; - int s, state; - OM_uint32 maj_stat, min_stat; - gss_name_t src_name, targ_name; - gss_buffer_desc sname, tname; - OM_uint32 lifetime; - gss_OID mechanism, name_type; - int is_local; -#ifdef GSSAPI_V2 - OM_uint32 context_flags; - int is_open; - gss_qop_t qop_state; - gss_OID_set mech_names; - gss_buffer_desc oid_name; -#else /* GSSAPI_V2 */ - int context_flags; -#endif /* GSSAPI_V2 */ - - /* Open connection */ - if ((s = connect_to_server(host, port)) < 0) - return -1; - - /* Establish context */ - if (client_establish_context(s, service_name, &context) < 0) - return -1; - -#ifdef GSSAPI_V2 - if (dov2) { - /* - * Attempt to save and then restore the context. - */ - maj_stat = gss_export_sec_context(&min_stat, - &context, - &context_token); - if (maj_stat != GSS_S_COMPLETE) { - display_status("exporting context", maj_stat, min_stat); - return -1; - } - maj_stat = gss_import_sec_context(&min_stat, - &context_token, - &context); - if (maj_stat != GSS_S_COMPLETE) { - display_status("importing context", maj_stat, min_stat); - return -1; - } - (void) gss_release_buffer(&min_stat, &context_token); - } -#endif /* GSSAPI_V2 */ - - /* Get context information */ - maj_stat = gss_inquire_context(&min_stat, context, - &src_name, &targ_name, &lifetime, - &mechanism, &context_flags, - &is_local -#ifdef GSSAPI_V2 - , &is_open -#endif /* GSSAPI_V2 */ - ); - if (maj_stat != GSS_S_COMPLETE) { - display_status("inquiring context", maj_stat, min_stat); - return -1; - } - - maj_stat = gss_display_name(&min_stat, src_name, &sname, - &name_type); - if (maj_stat != GSS_S_COMPLETE) { - display_status("displaying context", maj_stat, min_stat); - return -1; - } - maj_stat = gss_display_name(&min_stat, targ_name, &tname, - (gss_OID *) NULL); - if (maj_stat != GSS_S_COMPLETE) { - display_status("displaying context", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "\"%s\" to \"%s\", lifetime %d, flags %x, %s", - (char *) sname.value, (char *) tname.value, lifetime, - context_flags, - (is_local) ? "locally initiated" : "remotely initiated"); -#ifdef GSSAPI_V2 - fprintf(stderr, " %s", (is_open) ? "open" : "closed"); -#endif /* GSSAPI_V2 */ - fprintf(stderr, "\n"); - - (void) gss_release_name(&min_stat, &src_name); - (void) gss_release_name(&min_stat, &targ_name); - (void) gss_release_buffer(&min_stat, &sname); - (void) gss_release_buffer(&min_stat, &tname); - -#ifdef GSSAPI_V2 - if (dov2) { - size_t i; - - maj_stat = gss_oid_to_str(&min_stat, - name_type, - &oid_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "Name type of source name is %s.\n", - (char *) oid_name.value); - (void) gss_release_buffer(&min_stat, &oid_name); - (void) gss_release_oid(&min_stat, &name_type); - - /* Now get the names supported by the mechanism */ - maj_stat = gss_inquire_names_for_mech(&min_stat, - mechanism, - &mech_names); - if (maj_stat != GSS_S_COMPLETE) { - display_status("inquiring mech names", maj_stat, min_stat); - return -1; - } - - maj_stat = gss_oid_to_str(&min_stat, - mechanism, - &oid_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "Mechanism %s supports %d names\n", - (char *) oid_name.value, mech_names->count); - (void) gss_release_buffer(&min_stat, &oid_name); - for (i=0; icount; i++) { - gss_OID tmpoid; - int is_present; - - maj_stat = gss_oid_to_str(&min_stat, - &mech_names->elements[i], - &oid_name); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting oid->string", maj_stat, min_stat); - return -1; - } - fprintf(stderr, "%d: %s\n", i, (char *) oid_name.value); - - maj_stat = gss_str_to_oid(&min_stat, - &oid_name, - &tmpoid); - if (maj_stat != GSS_S_COMPLETE) { - display_status("converting string->oid", maj_stat, min_stat); - return -1; - } - - maj_stat = gss_test_oid_set_member(&min_stat, - tmpoid, - mech_names, - &is_present); - if (maj_stat != GSS_S_COMPLETE) { - display_status("testing oid presence", maj_stat, min_stat); - return -1; - } - if (!is_present) { - fprintf(stderr, "%s is not present in list?\n", - (char *) oid_name.value); - } - (void) gss_release_oid(&min_stat, &tmpoid); - (void) gss_release_buffer(&min_stat, &oid_name); - } - - (void) gss_release_oid_set(&min_stat, &mech_names); - (void) gss_release_oid(&min_stat, &mechanism); - } -#endif /* GSSAPI_V2 */ - - if (use_file) { - read_file(msg, &in_buf); - } else { - /* Seal the message */ - in_buf.value = msg; - in_buf.length = strlen(msg) + 1; - } -#ifdef GSSAPI_V2 - if (dov2) - maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT, - &in_buf, &state, &out_buf); - else -#endif /* GSSAPI_V2 */ - maj_stat = gss_seal(&min_stat, context, 1, GSS_C_QOP_DEFAULT, - &in_buf, &state, &out_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("sealing message", maj_stat, min_stat); - return -1; - } else if (! state) { - fprintf(stderr, "Warning! Message not encrypted.\n"); - } - - /* Send to server */ - if (send_token(s, &out_buf) < 0) - return -1; - (void) gss_release_buffer(&min_stat, &out_buf); - - /* Read signature block into out_buf */ - if (recv_token(s, &out_buf) < 0) - return -1; - - /* Verify signature block */ -#ifdef GSSAPI_V2 - if (dov2) - maj_stat = gss_verify_mic(&min_stat, context, &in_buf, - &out_buf, &qop_state); - else -#endif /* GSSAPI_V2 */ - maj_stat = gss_verify(&min_stat, context, &in_buf, &out_buf, &state); - if (maj_stat != GSS_S_COMPLETE) { - display_status("verifying signature", maj_stat, min_stat); - return -1; - } - (void) gss_release_buffer(&min_stat, &out_buf); - if (use_file) - free(in_buf.value); - - printf("Signature verified.\n"); - - /* Delete context */ - maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("deleting context", maj_stat, min_stat); - return -1; - } - (void) gss_release_buffer(&min_stat, &out_buf); - - return 0; -} - /* * Function: connect_to_server * @@ -403,6 +86,7 @@ int connect_to_server(host, port) } if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { perror("connecting to server"); + (void) close(s); return -1; } return s; @@ -419,6 +103,7 @@ int connect_to_server(host, port) * s (r) an established TCP connection to the service * service_name (r) the ASCII service name of the service * context (w) the established GSS-API context + * ret_flags (w) the returned flags from init_sec_context * * Returns: 0 on success, -1 on failure * @@ -434,10 +119,13 @@ int connect_to_server(host, port) * unsuccessful, the GSS-API error messages are displayed on stderr * and -1 is returned. */ -int client_establish_context(s, service_name, gss_context) +int client_establish_context(s, service_name, deleg_flag, + gss_context, ret_flags) int s; char *service_name; + OM_uint32 deleg_flag; gss_ctx_id_t *gss_context; + OM_uint32 *ret_flags; { gss_buffer_desc send_tok, recv_tok, *token_ptr; gss_name_t target_name; @@ -489,7 +177,7 @@ int client_establish_context(s, service_name, gss_context) token_ptr, NULL, /* ignore mech type */ &send_tok, - NULL, /* ignore ret_flags */ + ret_flags, NULL); /* ignore time_rec */ if (token_ptr != GSS_C_NO_BUFFER) @@ -527,7 +215,6 @@ int client_establish_context(s, service_name, gss_context) return 0; } - void read_file(file_name, in_buf) char *file_name; gss_buffer_t in_buf; @@ -566,3 +253,251 @@ void read_file(file_name, in_buf) bytes_in, count); } +/* + * Function: call_server + * + * Purpose: Call the "sign" service. + * + * Arguments: + * + * host (r) the host providing the service + * port (r) the port to connect to on host + * service_name (r) the GSS-API service name to authenticate to + * msg (r) the message to have "signed" + * + * Returns: 0 on success, -1 on failure + * + * Effects: + * + * call_server opens a TCP connection to and establishes a + * GSS-API context with service_name over the connection. It then + * seals msg in a GSS-API token with gss_seal, sends it to the server, + * reads back a GSS-API signature block for msg from the server, and + * verifies it with gss_verify. -1 is returned if any step fails, + * otherwise 0 is returned. + */ +int call_server(host, port, service_name, deleg_flag, msg, use_file) + char *host; + u_short port; + char *service_name; + OM_uint32 deleg_flag; + char *msg; + int use_file; +{ + gss_ctx_id_t context; + gss_buffer_desc in_buf, out_buf; + int s, state; + OM_uint32 ret_flags; + OM_uint32 maj_stat, min_stat; + gss_name_t src_name, targ_name; + gss_buffer_desc sname, tname; + OM_uint32 lifetime; + gss_OID mechanism, name_type; + int is_local; + OM_uint32 context_flags; + int is_open; + gss_qop_t qop_state; + gss_OID_set mech_names; + gss_buffer_desc oid_name; + size_t i; + + /* Open connection */ + if ((s = connect_to_server(host, port)) < 0) + return -1; + + /* Establish context */ + if (client_establish_context(s, service_name, deleg_flag, &context, + &ret_flags) < 0) { + (void) close(s); + return -1; + } + + /* display the flags */ + display_ctx_flags(ret_flags); + + /* Get context information */ + maj_stat = gss_inquire_context(&min_stat, context, + &src_name, &targ_name, &lifetime, + &mechanism, &context_flags, + &is_local, + &is_open); + if (maj_stat != GSS_S_COMPLETE) { + display_status("inquiring context", maj_stat, min_stat); + return -1; + } + + maj_stat = gss_display_name(&min_stat, src_name, &sname, + &name_type); + if (maj_stat != GSS_S_COMPLETE) { + display_status("displaying source name", maj_stat, min_stat); + return -1; + } + maj_stat = gss_display_name(&min_stat, targ_name, &tname, + (gss_OID *) NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("displaying target name", maj_stat, min_stat); + return -1; + } + fprintf(stderr, "\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", + (int) sname.length, (char *) sname.value, + (int) tname.length, (char *) tname.value, lifetime, + context_flags, + (is_local) ? "locally initiated" : "remotely initiated", + (is_open) ? "open" : "closed"); + + (void) gss_release_name(&min_stat, &src_name); + (void) gss_release_name(&min_stat, &targ_name); + (void) gss_release_buffer(&min_stat, &sname); + (void) gss_release_buffer(&min_stat, &tname); + + maj_stat = gss_oid_to_str(&min_stat, + name_type, + &oid_name); + if (maj_stat != GSS_S_COMPLETE) { + display_status("converting oid->string", maj_stat, min_stat); + return -1; + } + fprintf(stderr, "Name type of source name is %.*s.\n", + (int) oid_name.length, (char *) oid_name.value); + (void) gss_release_buffer(&min_stat, &oid_name); + + /* Now get the names supported by the mechanism */ + maj_stat = gss_inquire_names_for_mech(&min_stat, + mechanism, + &mech_names); + if (maj_stat != GSS_S_COMPLETE) { + display_status("inquiring mech names", maj_stat, min_stat); + return -1; + } + + maj_stat = gss_oid_to_str(&min_stat, + mechanism, + &oid_name); + if (maj_stat != GSS_S_COMPLETE) { + display_status("converting oid->string", maj_stat, min_stat); + return -1; + } + fprintf(stderr, "Mechanism %.*s supports %d names\n", + (int) oid_name.length, (char *) oid_name.value, + mech_names->count); + (void) gss_release_buffer(&min_stat, &oid_name); + + for (i=0; icount; i++) { + maj_stat = gss_oid_to_str(&min_stat, + &mech_names->elements[i], + &oid_name); + if (maj_stat != GSS_S_COMPLETE) { + display_status("converting oid->string", maj_stat, min_stat); + return -1; + } + fprintf(stderr, " %d: %.*s\n", i, + (int) oid_name.length, (char *) oid_name.value); + + (void) gss_release_buffer(&min_stat, &oid_name); + } + (void) gss_release_oid_set(&min_stat, &mech_names); + + if (use_file) { + read_file(msg, &in_buf); + } else { + /* Seal the message */ + in_buf.value = msg; + in_buf.length = strlen(msg) + 1; + } + + maj_stat = gss_wrap(&min_stat, context, 1, GSS_C_QOP_DEFAULT, + &in_buf, &state, &out_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("sealing message", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } else if (! state) { + fprintf(stderr, "Warning! Message not encrypted.\n"); + } + + /* Send to server */ + if (send_token(s, &out_buf) < 0) { + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + (void) gss_release_buffer(&min_stat, &out_buf); + + /* Read signature block into out_buf */ + if (recv_token(s, &out_buf) < 0) { + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + + /* Verify signature block */ + maj_stat = gss_verify_mic(&min_stat, context, &in_buf, + &out_buf, &qop_state); + if (maj_stat != GSS_S_COMPLETE) { + display_status("verifying signature", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + (void) gss_release_buffer(&min_stat, &out_buf); + + if (use_file) + free(in_buf.value); + + printf("Signature verified.\n"); + + /* Delete context */ + maj_stat = gss_delete_sec_context(&min_stat, &context, &out_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("deleting context", maj_stat, min_stat); + (void) close(s); + (void) gss_delete_sec_context(&min_stat, &context, GSS_C_NO_BUFFER); + return -1; + } + + (void) gss_release_buffer(&min_stat, &out_buf); + (void) close(s); + return 0; +} + +int main(argc, argv) + int argc; + char **argv; +{ + char *service_name, *server_host, *msg; + u_short port = 4444; + int use_file = 0; + OM_uint32 deleg_flag = 0; + + display_file = stdout; + + /* Parse arguments. */ + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-port") == 0) { + argc--; argv++; + if (!argc) usage(); + port = atoi(*argv); + } else if (strcmp(*argv, "-d") == 0) { + deleg_flag = GSS_C_DELEG_FLAG; + } else if (strcmp(*argv, "-f") == 0) { + use_file = 1; + } else + break; + argc--; argv++; + } + if (argc != 3) + usage(); + + server_host = *argv++; + service_name = *argv++; + msg = *argv++; + + if (call_server(server_host, port, service_name, + deleg_flag, msg, use_file) < 0) + exit(1); + + return 0; +} + diff --git a/src/appl/gss-sample/gss-misc.c b/src/appl/gss-sample/gss-misc.c index 446aa3087..67e898c1d 100644 --- a/src/appl/gss-sample/gss-misc.c +++ b/src/appl/gss-sample/gss-misc.c @@ -20,16 +20,21 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + #include #include #include +#include #ifdef HAVE_UNISTD_H #include #endif #include -#include #include +#include "gss-misc.h" #ifdef HAVE_STDLIB_H #include @@ -37,9 +42,48 @@ extern char *malloc(); #endif -static void display_status_1(); +FILE *display_file; + +static void display_status_1 + PROTOTYPE( (char *m, OM_uint32 code, int type) ); -FILE *display_file = NULL; +static int write_all(int fildes, char *buf, unsigned int nbyte) +{ + int ret; + char *ptr; + + for (ptr = buf; nbyte; ptr += ret, nbyte -= ret) { + ret = write(fildes, ptr, nbyte); + if (ret < 0) { + if (errno == EINTR) + continue; + return(ret); + } else if (ret == 0) { + return(ptr-buf); + } + } + + return(ptr-buf); +} + +static int read_all(int fildes, char *buf, unsigned int nbyte) +{ + int ret; + char *ptr; + + for (ptr = buf; nbyte; ptr += ret, nbyte -= ret) { + ret = read(fildes, ptr, nbyte); + if (ret < 0) { + if (errno == EINTR) + continue; + return(ret); + } else if (ret == 0) { + return(ptr-buf); + } + } + + return(ptr-buf); +} /* * Function: send_token @@ -67,7 +111,7 @@ int send_token(s, tok) len = htonl(tok->length); - ret = write(s, (char *) &len, 4); + ret = write_all(s, (char *) &len, 4); if (ret < 0) { perror("sending token length"); return -1; @@ -79,7 +123,7 @@ int send_token(s, tok) return -1; } - ret = write(s, tok->value, tok->length); + ret = write_all(s, tok->value, tok->length); if (ret < 0) { perror("sending token data"); return -1; @@ -120,9 +164,8 @@ int recv_token(s, tok) gss_buffer_t tok; { int ret; - int readsofar = 0; - ret = read(s, (char *) &tok->length, 4); + ret = read_all(s, (char *) &tok->length, 4); if (ret < 0) { perror("reading token length"); return -1; @@ -143,20 +186,45 @@ int recv_token(s, tok) return -1; } - while (readsofar < tok->length) { - ret = read(s, (char *) tok->value + readsofar, - tok->length - readsofar); - readsofar += ret; - if (ret < 0) { - perror("reading token data"); - free(tok->value); - return -1; - } + ret = read_all(s, (char *) tok->value, tok->length); + if (ret < 0) { + perror("reading token data"); + free(tok->value); + return -1; + } else if (ret != tok->length) { + fprintf(stderr, "sending token data: %d of %d bytes written\n", + ret, tok->length); + free(tok->value); + return -1; } return 0; } +static void display_status_1(m, code, type) + char *m; + OM_uint32 code; + int type; +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (display_file) + fprintf(display_file, "GSS-API error %s: %s\n", m, + (char *)msg.value); + (void) gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } +} + /* * Function: display_status * @@ -183,32 +251,37 @@ void display_status(msg, maj_stat, min_stat) display_status_1(msg, min_stat, GSS_C_MECH_CODE); } -static void display_status_1(m, code, type) - char *m; - OM_uint32 code; - int type; +/* + * Function: display_ctx_flags + * + * Purpose: displays the flags returned by context initation in + * a human-readable form + * + * Arguments: + * + * int ret_flags + * + * Effects: + * + * Strings corresponding to the context flags are printed on + * stdout, preceded by "context flag: " and followed by a newline + */ + +void display_ctx_flags(flags) + OM_uint32 flags; { - OM_uint32 maj_stat, min_stat; - gss_buffer_desc msg; -#ifdef GSSAPI_V2 - OM_uint32 msg_ctx; -#else /* GSSAPI_V2 */ - int msg_ctx; -#endif /* GSSAPI_V2 */ - - msg_ctx = 0; - while (1) { - maj_stat = gss_display_status(&min_stat, code, - type, GSS_C_NULL_OID, - &msg_ctx, &msg); - if (display_file) - fprintf(display_file, "GSS-API error %s: %s\n", m, - (char *)msg.value); - (void) gss_release_buffer(&min_stat, &msg); - - if (!msg_ctx) - break; - } + if (flags & GSS_C_DELEG_FLAG) + fprintf(display_file, "context flag: GSS_C_DELEG_FLAG\n"); + if (flags & GSS_C_MUTUAL_FLAG) + fprintf(display_file, "context flag: GSS_C_MUTUAL_FLAG\n"); + if (flags & GSS_C_REPLAY_FLAG) + fprintf(display_file, "context flag: GSS_C_REPLAY_FLAG\n"); + if (flags & GSS_C_SEQUENCE_FLAG) + fprintf(display_file, "context flag: GSS_C_SEQUENCE_FLAG\n"); + if (flags & GSS_C_CONF_FLAG ) + fprintf(display_file, "context flag: GSS_C_CONF_FLAG \n"); + if (flags & GSS_C_INTEG_FLAG ) + fprintf(display_file, "context flag: GSS_C_INTEG_FLAG \n"); } void print_token(tok) @@ -228,24 +301,3 @@ void print_token(tok) fprintf(display_file, "\n"); fflush(display_file); } - -void display_buffer(buffer) - gss_buffer_desc buffer; -{ - char *namebuf; - - if (!display_file) - return; - namebuf = malloc(buffer.length+1); - if (!namebuf) { - fprintf(stderr, "display_buffer: couldn't allocate buffer!\n"); - exit(1); - } - strncpy(namebuf, buffer.value, buffer.length); - namebuf[buffer.length] = '\0'; - fprintf(display_file, "%s", namebuf); - free(namebuf); -} - - - diff --git a/src/appl/gss-sample/gss-misc.h b/src/appl/gss-sample/gss-misc.h new file mode 100644 index 000000000..bfdcad2f4 --- /dev/null +++ b/src/appl/gss-sample/gss-misc.h @@ -0,0 +1,46 @@ +/* + * Copyright 1994 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#ifndef _GSSMISC_H_ +#define _GSSMISC_H_ + +#include +#include + +extern FILE *display_file; + +int send_token + PROTOTYPE( (int s, gss_buffer_t tok) ); +int recv_token + PROTOTYPE( (int s, gss_buffer_t tok) ); +void display_status + PROTOTYPE( (char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) ); +void display_ctx_flags + PROTOTYPE( (OM_uint32 flags) ); +void print_token + PROTOTYPE( (gss_buffer_t tok) ); + +#endif diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c index f685ab70f..bd4e7fe90 100644 --- a/src/appl/gss-sample/gss-server.c +++ b/src/appl/gss-sample/gss-server.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + #include #include #include @@ -29,11 +33,9 @@ #endif #include #include -#include -#include -#include #include +#include "gss-misc.h" #ifdef USE_STRING_H #include @@ -41,292 +43,16 @@ #include #endif -int create_socket(); - -int send_token(); -int recv_token(); -void display_status(); -int test_import_export_context(); -void print_token(); - -int server_acquire_creds(); -int server_establish_context(); -int sign_server(); - -extern FILE *display_file; -FILE *log; - -int verbose = 0; - -void usage() { - fprintf(stderr, "Usage: gss-server [-port port] [-v2] [-inetd] [-logfile file] service_name\n"); + fprintf(stderr, "Usage: gss-server [-port port] [-verbose]\n"); + fprintf(stderr, " [-inetd] [-logfile file] [service_name]\n"); exit(1); } -int -main(argc, argv) - int argc; - char **argv; -{ - char *service_name; - u_short port = 4444; - int s; - int do_inetd = 0; - int dov2 = 0; - int once = 0; - - log = stdout; - display_file = stdout; - argc--; argv++; - while (argc) { - if (strcmp(*argv, "-port") == 0) { - argc--; argv++; - if (!argc) usage(); - port = atoi(*argv); - } else if (strcmp(*argv, "-inetd") == 0) { - do_inetd = 1; - display_file = 0; - } else if (strcmp(*argv, "-verbose") == 0) { - verbose = 1; - } else if (strcmp(*argv, "-v2") == 0) { - dov2 = 1; - } else if (strcmp(*argv, "-once") == 0) { - once = 1; - } else if (strcmp(*argv, "-logfile") == 0) { - argc--; argv++; - if (!argc) usage(); - log = fopen(*argv, "a"); - display_file = log; - if (!log) { - perror(*argv); - exit(1); - } - } else - break; - argc--; argv++; - } - if (argc != 1) - usage(); - - service_name = *argv; - - if (do_inetd == 0) { - if ((s = create_socket(port)) < 0) - exit(1); - } else { - s = -1; - close(1); - close(2); - } - - if (sign_server(s, service_name, dov2, once) < 0) - exit(1); - - /*NOTREACHED*/ - return 0; -} - -/* - * Function: create_socket - * - * Purpose: Opens a listening TCP socket. - * - * Arguments: - * - * port (r) the port number on which to listen - * - * Returns: the listening socket file descriptor, or -1 on failure - * - * Effects: - * - * A listening socket on the specified port and created and returned. - * On error, an error message is displayed and -1 is returned. - */ -int create_socket(port) - u_short port; -{ - struct sockaddr_in saddr; - int s; - int on = 1; - - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - saddr.sin_addr.s_addr = INADDR_ANY; - - if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("creating socket"); - return -1; - } - /* Let the socket be reused right away */ - (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { - perror("binding socket"); - return -1; - } - if (listen(s, 5) < 0) { - perror("listening on socket"); - return -1; - } - return s; -} - -/* - * Function: sign_server - * - * Purpose: Performs the "sign" service. - * - * Arguments: - * - * s (r) a TCP socket on which to listen for connections. - * If s is -1, then assume that we were started out of - * inetd and use file descriptor 0. - * service_name (r) the ASCII name of the GSS-API service to - * establish a context as - * dov2 (r) a boolean indicating whether we should use GSSAPI - * V2 interfaces, if available. - * once (r) a boolean indicating whether we should - * only accept one connection, then exit. - * - * Returns: -1 on error - * - * Effects: - * - * sign_server acquires GSS-API credentials for service_name and then - * loops forever accepting TCP connections on s, establishing a - * context, and performing a single sign request. - * - * A sign request is a single GSS-API sealed token. The token is - * unsealed and a signature block, produced with gss_sign, is returned - * to the sender. The context is the destroyed and the connection - * closed. - * - * If any error occurs, -1 is returned. - */ -int sign_server(s, service_name, dov2, once) - int s; - char *service_name; - int dov2; - int once; -{ - gss_cred_id_t server_creds; - gss_buffer_desc client_name, xmit_buf, msg_buf; - gss_ctx_id_t context; - OM_uint32 maj_stat, min_stat; - int i,s2; - time_t now; - char *cp; - - if (server_acquire_creds(service_name, &server_creds) < 0) - return -1; - - while (1) { - if (s >= 0) { - /* Accept a TCP connection */ - if ((s2 = accept(s, NULL, 0)) < 0) { - perror("accepting connection"); - exit(1); - } - } else - s2 = 0; - - /* Establish a context with the client */ - if (server_establish_context(s2, server_creds, &context, - &client_name) < 0) - break; - - time(&now); - fprintf(log, "Accepted connection: \"%s\" at %s", - (char *) client_name.value, ctime(&now)); - (void) gss_release_buffer(&min_stat, &client_name); - - if (dov2) { - for (i=0; i < 3; i++) - if (test_import_export_context(&context)) - break; - if (i < 3) - break; - } - - /* Receive the sealed message token */ - if (recv_token(s2, &xmit_buf) < 0) - break; - - if (verbose && log) { - fprintf(log, "Sealed message token:\n"); - print_token(xmit_buf); - } - -#ifdef GSSAPI_V2 - if (dov2) - maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, - (int *) NULL, (gss_qop_t *) NULL); - else -#endif /* GSSAPI_V2 */ - /* Unseal the message token */ - maj_stat = gss_unseal(&min_stat, context, &xmit_buf, - &msg_buf, NULL, NULL); - if (maj_stat != GSS_S_COMPLETE) { - display_status("unsealing message", maj_stat, min_stat); - break; - } - - (void) gss_release_buffer(&min_stat, &xmit_buf); - - fprintf(log, "Received message: "); - cp = msg_buf.value; - if (isprint(cp[0]) && isprint(cp[1])) - fprintf(log, "\"%s\"\n", cp); - else { - printf("\n"); - print_token(msg_buf); - } - - /* Produce a signature block for the message */ -#ifdef GSSAPI_V2 - if (dov2) - maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, - &msg_buf, &xmit_buf); - else -#endif /* GSSAPI_V2 */ - maj_stat = gss_sign(&min_stat, context, GSS_C_QOP_DEFAULT, - &msg_buf, &xmit_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("signing message", maj_stat, min_stat); - break; - } - - (void) gss_release_buffer(&min_stat, &msg_buf); - - /* Send the signature block to the client */ - if (send_token(s2, &xmit_buf) < 0) - break; - - (void) gss_release_buffer(&min_stat, &xmit_buf); - - /* Delete context */ - maj_stat = gss_delete_sec_context(&min_stat, &context, &xmit_buf); - if (maj_stat != GSS_S_COMPLETE) { - display_status("deleting context", maj_stat, min_stat); - break; - } - - (void) gss_release_buffer(&min_stat, &xmit_buf); - - /* Close TCP connection */ - close(s2); - - fflush(log); - - if (s < 0 || once) - break; - } +FILE *log; - /*NOTREACHED*/ - (void) gss_release_cred(&min_stat, &server_creds); - return -1; -} +int verbose = 0; /* * Function: server_acquire_creds @@ -400,17 +126,17 @@ int server_acquire_creds(service_name, server_creds) * in client_name and 0 is returned. If unsuccessful, an error * message is displayed and -1 is returned. */ -int server_establish_context(s, server_creds, context, client_name) +int server_establish_context(s, server_creds, context, client_name, ret_flags) int s; gss_cred_id_t server_creds; gss_ctx_id_t *context; gss_buffer_t client_name; + OM_uint32 *ret_flags; { gss_buffer_desc send_tok, recv_tok; gss_name_t client; gss_OID doid; OM_uint32 maj_stat, min_stat; - OM_uint32 ret_flags; *context = GSS_C_NO_CONTEXT; @@ -432,7 +158,7 @@ int server_establish_context(s, server_creds, context, client_name) &client, &doid, &send_tok, - &ret_flags, + ret_flags, NULL, /* ignore time_rec */ NULL); /* ignore del_cred_handle */ @@ -441,8 +167,9 @@ int server_establish_context(s, server_creds, context, client_name) (void) gss_release_buffer(&min_stat, &recv_tok); return -1; } + (void) gss_release_buffer(&min_stat, &recv_tok); - + if (send_tok.length != 0) { if (verbose && log) { fprintf(log, @@ -457,15 +184,18 @@ int server_establish_context(s, server_creds, context, client_name) (void) gss_release_buffer(&min_stat, &send_tok); } - if (maj_stat == GSS_S_CONTINUE_NEEDED) - if (log) - fprintf(log, "continue needed..."); if (log) { - fprintf(log, "\n"); + if (maj_stat == GSS_S_CONTINUE_NEEDED) + fprintf(log, "\n"); + else + fprintf(log, "continue needed...\n"); fflush(log); } } while (maj_stat == GSS_S_CONTINUE_NEEDED); + /* display the flags */ + display_ctx_flags(*ret_flags); + maj_stat = gss_display_name(&min_stat, client, client_name, &doid); if (maj_stat != GSS_S_COMPLETE) { display_status("displaying name", maj_stat, min_stat); @@ -479,42 +209,232 @@ int server_establish_context(s, server_creds, context, client_name) return 0; } -static float timeval_subtract(tv1, tv2) - struct timeval *tv1, *tv2; +/* + * Function: create_socket + * + * Purpose: Opens a listening TCP socket. + * + * Arguments: + * + * port (r) the port number on which to listen + * + * Returns: the listening socket file descriptor, or -1 on failure + * + * Effects: + * + * A listening socket on the specified port and created and returned. + * On error, an error message is displayed and -1 is returned. + */ +int create_socket(port) + u_short port; { - return ((tv1->tv_sec - tv2->tv_sec) + - ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000); + struct sockaddr_in saddr; + int s; + int on = 1; + + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + saddr.sin_addr.s_addr = INADDR_ANY; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("creating socket"); + return -1; + } + /* Let the socket be reused right away */ + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + if (bind(s, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { + perror("binding socket"); + (void) close(s); + return -1; + } + if (listen(s, 5) < 0) { + perror("listening on socket"); + (void) close(s); + return -1; + } + return s; } -int test_import_export_context(context) - gss_ctx_id_t *context; +/* + * Function: sign_server + * + * Purpose: Performs the "sign" service. + * + * Arguments: + * + * s (r) a TCP socket on which a connection has been + * accept()ed + * service_name (r) the ASCII name of the GSS-API service to + * establish a context as + * + * Returns: -1 on error + * + * Effects: + * + * sign_server establishes a context, and performs a single sign request. + * + * A sign request is a single GSS-API sealed token. The token is + * unsealed and a signature block, produced with gss_sign, is returned + * to the sender. The context is the destroyed and the connection + * closed. + * + * If any error occurs, -1 is returned. + */ +int sign_server(s, server_creds) + int s; + gss_cred_id_t server_creds; { - OM_uint32 min_stat, maj_stat; - gss_buffer_desc context_token; - struct timeval tm1, tm2; - - /* - * Attempt to save and then restore the context. - */ - gettimeofday(&tm1, (struct timezone *)0); - maj_stat = gss_export_sec_context(&min_stat, context, &context_token); - if (maj_stat != GSS_S_COMPLETE) { - display_status("exporting context", maj_stat, min_stat); - return 1; - } - gettimeofday(&tm2, (struct timezone *)0); - if (verbose && log) - fprintf(log, "Exported context: %d bytes, %7.4f seconds\n", - context_token.length, timeval_subtract(&tm2, &tm1)); - maj_stat = gss_import_sec_context(&min_stat, &context_token, context); - if (maj_stat != GSS_S_COMPLETE) { - display_status("importing context", maj_stat, min_stat); - return 1; - } - gettimeofday(&tm1, (struct timezone *)0); - if (verbose && log) - fprintf(log, "Importing context: %7.4f seconds\n", - timeval_subtract(&tm1, &tm2)); - (void) gss_release_buffer(&min_stat, &context_token); - return 0; + gss_buffer_desc client_name, xmit_buf, msg_buf; + gss_ctx_id_t context; + OM_uint32 maj_stat, min_stat; + int i, conf_state, ret_flags; + char *cp; + + /* Establish a context with the client */ + if (server_establish_context(s, server_creds, &context, + &client_name, &ret_flags) < 0) + return(-1); + + printf("Accepted connection: \"%.*s\"\n", + client_name.length, client_name.value); + (void) gss_release_buffer(&min_stat, &client_name); + + /* Receive the sealed message token */ + if (recv_token(s, &xmit_buf) < 0) + return(-1); + + if (verbose && log) { + fprintf(log, "Sealed message token:\n"); + print_token(&xmit_buf); + } + + maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf, + &conf_state, (gss_qop_t *) NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("unsealing message", maj_stat, min_stat); + return(-1); + } else if (! conf_state) { + fprintf(stderr, "Warning! Message not encrypted.\n"); + } + + (void) gss_release_buffer(&min_stat, &xmit_buf); + + fprintf(log, "Received message: "); + cp = msg_buf.value; + if (isprint(cp[0]) && isprint(cp[1])) + fprintf(log, "\"%s\"\n", cp); + else { + printf("\n"); + print_token(&msg_buf); + } + + /* Produce a signature block for the message */ + maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT, + &msg_buf, &xmit_buf); + if (maj_stat != GSS_S_COMPLETE) { + display_status("signing message", maj_stat, min_stat); + return(-1); + } + + (void) gss_release_buffer(&min_stat, &msg_buf); + + /* Send the signature block to the client */ + if (send_token(s, &xmit_buf) < 0) + return(-1); + + (void) gss_release_buffer(&min_stat, &xmit_buf); + + /* Delete context */ + maj_stat = gss_delete_sec_context(&min_stat, &context, NULL); + if (maj_stat != GSS_S_COMPLETE) { + display_status("deleting context", maj_stat, min_stat); + return(-1); + } + + fflush(log); + + return(0); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + char *service_name; + gss_cred_id_t server_creds; + OM_uint32 min_stat; + u_short port = 4444; + int s; + int once = 0; + int do_inetd = 0; + + log = stdout; + display_file = stdout; + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-port") == 0) { + argc--; argv++; + if (!argc) usage(); + port = atoi(*argv); + } else if (strcmp(*argv, "-verbose") == 0) { + verbose = 1; + } else if (strcmp(*argv, "-once") == 0) { + once = 1; + } else if (strcmp(*argv, "-inetd") == 0) { + do_inetd = 1; + } else if (strcmp(*argv, "-logfile") == 0) { + argc--; argv++; + if (!argc) usage(); + log = fopen(*argv, "a"); + display_file = log; + if (!log) { + perror(*argv); + exit(1); + } + } else + break; + argc--; argv++; + } + if (argc != 1) + usage(); + + if ((*argv)[0] == '-') + usage(); + + service_name = *argv; + + if (server_acquire_creds(service_name, &server_creds) < 0) + return -1; + + if (do_inetd) { + close(1); + close(2); + + sign_server(0, server_creds); + close(0); + } else { + int stmp; + + if (stmp = create_socket(port)) { + do { + /* Accept a TCP connection */ + if ((s = accept(stmp, NULL, 0)) < 0) { + perror("accepting connection"); + } else { + /* this return value is not checked, because there's + not really anything to do if it fails */ + sign_server(s, server_creds); + } + } while (!once); + } + + close(stmp); + } + + (void) gss_release_cred(&min_stat, &server_creds); + + /*NOTREACHED*/ + (void) close(s); + return 0; } diff --git a/src/appl/gssftp/ftp/ChangeLog b/src/appl/gssftp/ftp/ChangeLog index 0d480b2c6..d73bf4c69 100644 --- a/src/appl/gssftp/ftp/ChangeLog +++ b/src/appl/gssftp/ftp/ChangeLog @@ -1,3 +1,8 @@ +Wed Jul 10 16:40:19 1996 Marc Horowitz + + * cmdtab.c (cmdtab[]), cmds.c (delete_file): rename delete() to + delete_file() to avoid conflict with the dbm delete() function + Thu Mar 28 21:07:40 1996 Ken Raeburn * cmds.c (setpeer): Define unix for HP-UX. @@ -13,7 +18,7 @@ Thu Mar 28 19:26:53 1996 Marc Horowitz Mon Mar 18 12:12:44 1996 Ezra Peisach * secure.c, ftp.c, ftp_var.h: Define STDARG if HAVE_STDARG_H is - defined (in addition to the other tests) + defined (in addition to the other tests) * configure.in: Add AC_HEADER_STDARG diff --git a/src/appl/gssftp/ftp/cmds.c b/src/appl/gssftp/ftp/cmds.c index 396f317dd..d0dc5dda6 100644 --- a/src/appl/gssftp/ftp/cmds.c +++ b/src/appl/gssftp/ftp/cmds.c @@ -1243,7 +1243,7 @@ lcd(argc, argv) /* * Delete a single file. */ -delete(argc, argv) +delete_file(argc, argv) int argc; char *argv[]; { diff --git a/src/appl/gssftp/ftp/cmdtab.c b/src/appl/gssftp/ftp/cmdtab.c index 39d171d84..f20660cac 100644 --- a/src/appl/gssftp/ftp/cmdtab.c +++ b/src/appl/gssftp/ftp/cmdtab.c @@ -50,7 +50,7 @@ int setlevel(), setclear(), setsafe(); int setprivate(); #endif int disconnect(), restart(), reget(), syst(); -int cd(), lcd(), delete(), mdelete(), user(); +int cd(), lcd(), delete_file(), mdelete(), user(); int ls(), mls(), get(), mget(), help(), append(), put(), mput(); int quit(), renamefile(), status(); int quote(), rmthelp(), shell(), site(); @@ -154,7 +154,7 @@ struct cmd cmdtab[] = { { "clear", clearhelp, 0, 1, 1, setclear }, { "close", disconhelp, 0, 1, 1, disconnect }, { "cr", crhelp, 0, 0, 0, setcr }, - { "delete", deletehelp, 0, 1, 1, delete }, + { "delete", deletehelp, 0, 1, 1, delete_file }, { "debug", debughelp, 0, 0, 0, setdebug }, { "dir", dirhelp, 1, 1, 1, ls }, { "disconnect", disconhelp, 0, 1, 1, disconnect }, diff --git a/src/appl/gssftp/ftpd/ChangeLog b/src/appl/gssftp/ftpd/ChangeLog index 798b51dbd..3f04d2cee 100644 --- a/src/appl/gssftp/ftpd/ChangeLog +++ b/src/appl/gssftp/ftpd/ChangeLog @@ -1,3 +1,10 @@ +Wed Jul 10 16:38:01 1996 Marc Horowitz + + * ftpd.c (store), ftpcmd.y (STOR, APPE, STOU): rename store() to + store_file() to avoid conflict with dbm store() function + * ftpd.c (delete), ftpcmd.y (DELE): rename delete() to + delete_file() to avoid conflict with the dbm delete() function + Thu Jun 13 18:35:19 1996 Kevin L Mitchell * ftpd.c (authdata): misplaced braces caused server to not be able to diff --git a/src/appl/gssftp/ftpd/ftpcmd.y b/src/appl/gssftp/ftpd/ftpcmd.y index c891ab913..c014f35b2 100644 --- a/src/appl/gssftp/ftpd/ftpcmd.y +++ b/src/appl/gssftp/ftpd/ftpcmd.y @@ -338,14 +338,14 @@ cmd: USER SP username CRLF | STOR check_login SP pathname CRLF = { if ($2 && $4 != NULL) - store((char *) $4, "w", 0); + store_file((char *) $4, "w", 0); if ($4 != NULL) free((char *) $4); } | APPE check_login SP pathname CRLF = { if ($2 && $4 != NULL) - store((char *) $4, "a", 0); + store_file((char *) $4, "a", 0); if ($4 != NULL) free((char *) $4); } @@ -387,7 +387,7 @@ cmd: USER SP username CRLF | DELE check_login SP pathname CRLF = { if ($2 && $4 != NULL) - delete((char *) $4); + delete_file((char *) $4); if ($4 != NULL) free((char *) $4); } @@ -535,7 +535,7 @@ cmd: USER SP username CRLF | STOU check_login SP pathname CRLF = { if ($2 && $4 != NULL) - store((char *) $4, "w", 1); + store_file((char *) $4, "w", 1); if ($4 != NULL) free((char *) $4); } diff --git a/src/appl/gssftp/ftpd/ftpd.c b/src/appl/gssftp/ftpd/ftpd.c index c260325f3..0d7044271 100644 --- a/src/appl/gssftp/ftpd/ftpd.c +++ b/src/appl/gssftp/ftpd/ftpd.c @@ -923,7 +923,7 @@ done: (*closefunc)(fin); } -store(name, mode, unique) +store_file(name, mode, unique) char *name, *mode; int unique; { @@ -1549,7 +1549,7 @@ yyerror(s) reply(500, "'%s': command not understood.", cbuf); } -delete(name) +delete_file(name) char *name; { struct stat st; diff --git a/src/appl/telnet/libtelnet/ChangeLog b/src/appl/telnet/libtelnet/ChangeLog index 1c131f1ae..7da90002f 100644 --- a/src/appl/telnet/libtelnet/ChangeLog +++ b/src/appl/telnet/libtelnet/ChangeLog @@ -1,3 +1,16 @@ +Tue Jul 9 14:59:19 1996 Marc Horowitz + + * Makefile.in (LOCALINCLUDES): use @KRB4_INCLUDES@ instead of an + explicit path to the in-tree krb4 headers + +Mon Jul 8 01:33:30 1996 Marc Horowitz + + * enc-proto.h (des_new_random_key, des_set_random_generator_seed, + des_key_sched, des_ecb_encrypt, des_string_to_key): removed these + declarations. these are kerberos/des symbols, and should not be + declared here. Two of these symbols (des_key_sched and + des_ecb_encrypt) conflict with CNS. + Fri Jun 14 19:09:48 1996 Sam Hartman * configure.in * Makefile.in (LOCALINCLUDES): Don't include KerberosIV; use whatever is appropriate for the withval @@ -15,7 +28,7 @@ Sat Apr 27 16:09:54 1996 Richard Basch Fri Apr 12 23:36:01 1996 Richard Basch * forward.c (rd_and_store_for_creds): Consistency with the - krlogind forwarded credentials cache naming scheme - krb5cc_p + krlogind forwarded credentials cache naming scheme - krb5cc_p Thu Apr 11 21:45:21 1996 Richard Basch diff --git a/src/appl/telnet/libtelnet/Makefile.in b/src/appl/telnet/libtelnet/Makefile.in index 02d4e757b..d3e9eb5a6 100644 --- a/src/appl/telnet/libtelnet/Makefile.in +++ b/src/appl/telnet/libtelnet/Makefile.in @@ -21,7 +21,7 @@ # AUTH_DEF=-DAUTHENTICATION -DENCRYPTION -DDES_ENCRYPTION -DKRB5 -DFORWARD \ -UNO_LOGIN_F -DLOGIN_CAP_F -DLOGIN_PROGRAM=KRB5_PATH_LOGIN -LOCALINCLUDES=-I.. -I$(srcdir)/.. +LOCALINCLUDES=-I.. -I$(srcdir)/.. @KRB4_INCLUDES@ CFLAGS = $(CCOPTS) $(AUTH_DEF) $(DEFS) $(LOCALINCLUDES) LIBOBJS=@LIBOBJS@ diff --git a/src/appl/telnet/libtelnet/enc-proto.h b/src/appl/telnet/libtelnet/enc-proto.h index 996a4f5d0..48f91430f 100644 --- a/src/appl/telnet/libtelnet/enc-proto.h +++ b/src/appl/telnet/libtelnet/enc-proto.h @@ -117,10 +117,4 @@ int ofb64_reply P((unsigned char *, int)); void ofb64_session P((Session_Key *, int)); int ofb64_keyid P((int, unsigned char *, int *)); void ofb64_printsub P((unsigned char *, int, unsigned char *, int)); - -int des_new_random_key P((Block)); -void des_set_random_generator_seed P((Block)); -void des_key_sched P((Block, Schedule)); -void des_ecb_encrypt P((Block, Block, Schedule, int)); -int des_string_to_key P((char *, Block)); #endif /* ENCRYPTION */ diff --git a/src/clients/kinit/ChangeLog b/src/clients/kinit/ChangeLog index 75f9fa852..7fa4f102b 100644 --- a/src/clients/kinit/ChangeLog +++ b/src/clients/kinit/ChangeLog @@ -9,6 +9,11 @@ Sun Jul 7 15:21:58 1996 Ezra Peisach and only new tgt is stored. (main): New option -R to renew tickets. +Thu Jun 20 20:19:44 1996 Marc Horowitz + + * kinit.M, kinit.c (main): Add a -s option to kinit which specifies + the service name to be used in the TGS_REQ. + Fri May 3 00:28:10 1996 Mark Eichin * kinit.c (krb5_validate_tgt): new function, takes a credential diff --git a/src/clients/kinit/kinit.M b/src/clients/kinit/kinit.M index 9d05b2d58..3108a0aad 100644 --- a/src/clients/kinit/kinit.M +++ b/src/clients/kinit/kinit.M @@ -94,6 +94,11 @@ option is not used, the default cache is used. Any contents of the cache are destroyed by .IR kinit . .PP +The +.B \-s +option can be used to specify an alternate service name to use when +getting initial tickets. +.PP The default credentials cache may vary between systems; however, if the .B KRB5CCNAME environment variable is set, its value is used to name the default diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c index 555b1b861..df8d449e4 100644 --- a/src/clients/kinit/kinit.c +++ b/src/clients/kinit/kinit.c @@ -60,6 +60,7 @@ main(argc, argv) krb5_ccache ccache = NULL; char *cache_name = NULL; /* -f option */ char *keytab_name = NULL; /* -t option */ + char *service_name = NULL; /* -s option */ krb5_deltat lifetime = KRB5_DEFAULT_LIFE; /* -l option */ krb5_timestamp starttime = 0; krb5_deltat rlife = 0; @@ -90,7 +91,7 @@ main(argc, argv) if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; - while ((option = getopt(argc, argv, "r:Rfpl:s:c:kt:v")) != EOF) { + while ((option = getopt(argc, argv, "r:Rfpl:s:c:kt:vS:")) != EOF) { switch (option) { case 'r': options |= KDC_OPT_RENEWABLE; @@ -108,6 +109,9 @@ main(argc, argv) /* validate the ticket */ options |= KDC_OPT_VALIDATE; break; + case 'S': + service_name = optarg; + break; case 'p': options |= KDC_OPT_PROXIABLE; break; @@ -186,7 +190,7 @@ main(argc, argv) } if (errflg) { - fprintf(stderr, "Usage: %s [-r time] [-R] [-s time] [-v] [-puf] [-l lifetime] [-c cachename] [-k] [-t keytab] [principal]\n", argv[0]); + fprintf(stderr, "Usage: %s [-r time] [-R] [-s time] [-v] [-puf] [-l lifetime] [-c cachename] [-k] [-t keytab] [-S target_service] [principal]\n", argv[0]); exit(2); } @@ -250,17 +254,25 @@ main(argc, argv) my_creds.client = me; - if((code = krb5_build_principal_ext(kcontext, &server, - krb5_princ_realm(kcontext, me)->length, - krb5_princ_realm(kcontext, me)->data, - tgtname.length, tgtname.data, - krb5_princ_realm(kcontext, me)->length, - krb5_princ_realm(kcontext, me)->data, - 0))) { - com_err(argv[0], code, "while building server name"); - exit(1); + if (service_name == NULL) { + if((code = krb5_build_principal_ext(kcontext, &server, + krb5_princ_realm(kcontext, me)->length, + krb5_princ_realm(kcontext, me)->data, + tgtname.length, tgtname.data, + krb5_princ_realm(kcontext, me)->length, + krb5_princ_realm(kcontext, me)->data, + 0))) { + com_err(argv[0], code, "while building server name"); + exit(1); + } + } else { + if (code = krb5_parse_name(kcontext, service_name, &server)) { + com_err(argv[0], code, "while parsing service name %s", + service_name); + exit(1); + } } - + my_creds.server = server; if (options & KDC_OPT_POSTDATED) { diff --git a/src/config/ChangeLog b/src/config/ChangeLog index 73323b938..fec5211fd 100644 --- a/src/config/ChangeLog +++ b/src/config/ChangeLog @@ -1,3 +1,8 @@ +Tue Jul 9 15:02:00 1996 Marc Horowitz + + * pre.in (SRVLIBS, SRVDEPLIBS, CLNTLIBS, CLNTDEPLIBS): added for + support of new aclocal.m4 KRB5_LIBRARIES macro + Thu Jun 13 23:02:23 1996 Tom Yu * post.in,pre.in: break some things out from aclocal.m4 and put diff --git a/src/config/pre.in b/src/config/pre.in index d5f8342f0..78d94e17a 100644 --- a/src/config/pre.in +++ b/src/config/pre.in @@ -38,6 +38,11 @@ LD_UNRESOLVED_PREFIX = @LD_UNRESOLVED_PREFIX@ LD_SHLIBDIR_PREFIX = @LD_SHLIBDIR_PREFIX@ LDARGS = @LDARGS@ LIBS = @LIBS@ +SRVLIBS = @SRVLIBS@ +SRVDEPLIBS = @SRVDEPLIBS@ +CLNTLIBS = @CLNTLIBS@ +CLNTDEPLIBS = @CLNTDEPLIBS@ + INSTALL=@INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ -s INSTALL_DATA=@INSTALL_DATA@ diff --git a/src/configure.in b/src/configure.in index 670a2c8d0..5885d1449 100644 --- a/src/configure.in +++ b/src/configure.in @@ -257,7 +257,8 @@ dnl WITH_ANAME_DB WITH_KDB_DB dnl -CONFIG_DIRS(util include lib kdc admin kadmin $kadminv4 $krb524 slave clients appl tests config-files) +CONFIG_DIRS(util include lib $krb524 kdc admin kadmin slave clients appl tests config-files) +dnl $kadminv4 removed from the above DO_SUBDIRS -dnl dnl AC_OUTPUT(Makefile,[EXTRA_RULES]) +dnl AC_OUTPUT(Makefile,[EXTRA_RULES]) V5_AC_OUTPUT_MAKEFILE diff --git a/src/include/ChangeLog b/src/include/ChangeLog index cba08f35e..8fa97791e 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -89,6 +89,11 @@ Tue Apr 30 14:51:55 1996 Macintosh definitions of PROVIDE_* since that is done in mac/libaries/KerberosHeaders.h. +Wed Apr 17 20:56:51 1996 Marc Horowitz + + * k5-int.h, port-sockets.h: moved socket stuff into a separate + file so that gssapi doesn't have to include k5-int.h + Thu Apr 11 23:50:24 1996 Theodore Y. Ts'o * krb5.hin (krb5_x, krb5_xc): Fix wrapper macros so they don't diff --git a/src/include/k5-int.h b/src/include/k5-int.h index e3d2057e2..bcc6c5ac0 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -365,85 +365,7 @@ typedef unsigned char u_char; #include "krb5.h" #ifdef NEED_SOCKETS -/* - * Begin "k5-sockets.h" - */ -#if defined (_MSDOS) || defined(_WIN32) - -#include - -/* Some of our own infrastructure where the WinSock stuff was too hairy - to dump into a clean Unix program... */ - -#define SOCKET_INITIALIZE() win_socket_initialize() -#define SOCKET_CLEANUP() WSACleanup() -#define SOCKET_ERRNO (WSAGetLastError()) -#define SOCKET_SET_ERRNO(x) (WSASetLastError (x)) -#define SOCKET_NFDS(f) (0) /* select()'s first arg is ignored */ -#define SOCKET_READ(fd, b, l) (recv(fd, b, l, 0)) -#define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) -#define SOCKET_EINTR WSAEINTR - -int win_socket_initialize(); - -#else /* not _MSDOS */ - -/* If this source file requires it, define struct sockaddr_in - (and possibly other things related to network I/O). */ - -#ifdef HAVE_MACSOCK_H /* Sockets stuff differs on Mac */ -#include "macsock.h" /* Macintosh sockets emulation library */ - -/* Some of our own infrastructure where the WinSock stuff was too hairy - to dump into a clean Unix program... */ - -#define SOCKET_INITIALIZE() (WSAStartup(0x0101, (WSADATA *)0)) -#define SOCKET_CLEANUP() (WSACleanup()) -#define SOCKET_ERRNO (WSAGetLastError()) -#define SOCKET_SET_ERRNO(x) (WSASetLastError(x)) -#define SOCKET_NFDS(f) (0) /* select()'s first arg is ignored */ -#define SOCKET_READ(fd, b, l) (recv(fd, b, l, 0)) -#define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) -#define SOCKET_EINTR WSAEINTR - -#else /* ! HAVE_MACSOCK_H */ /* Sockets stuff for Unix machines */ - -#include /* For struct sockaddr_in and in_addr */ -#include /* For inet_ntoa */ -#include /* For struct hostent, gethostbyname, etc */ -#include /* For MAXHOSTNAMELEN */ -#include /* For SOCK_*, AF_*, etc */ -#include /* For struct timeval */ -#include /* For struct ifconf, for localaddr.c */ - -/* - * Compatability with WinSock calls on MS-Windows... - */ -#define SOCKET unsigned int -#define INVALID_SOCKET ((SOCKET)~0) -#define closesocket close -#define ioctlsocket ioctl -#define SOCKET_ERROR (-1) - -/* Some of our own infrastructure where the WinSock stuff was too hairy - to dump into a clean Unix program... */ - -#define SOCKET_INITIALIZE() (0) /* No error (or anything else) */ -#define SOCKET_CLEANUP() /* nothing */ -#define SOCKET_ERRNO errno -#define SOCKET_SET_ERRNO(x) (errno = (x)) -#define SOCKET_NFDS(f) ((f)+1) /* select() arg for a single fd */ -#define SOCKET_READ read -#define SOCKET_WRITE write -#define SOCKET_EINTR EINTR - -#endif /* HAVE_MACSOCK_H */ - -#endif /* _MSDOS */ - -/* - * End "k5-sockets.h" - */ +#include "port-sockets.h" #endif /* krb5/krb5.h includes many other .h files in the krb5 subdirectory. diff --git a/src/include/krb5/ChangeLog b/src/include/krb5/ChangeLog index adc0fabd5..123349af2 100644 --- a/src/include/krb5/ChangeLog +++ b/src/include/krb5/ChangeLog @@ -1,3 +1,4 @@ +<<<<<<< ChangeLog Wed Jun 12 00:40:29 1996 Theodore Ts'o * adm_proto.h: Change usage of INTERFACE to use KRB5_CALLCONV and @@ -24,6 +25,12 @@ Sun May 19 14:32:19 1996 Sam Hartman * configure.in: Check for uid_t. +Sun May 12 00:46:05 1996 Marc Horowitz + + * kdb.h: convert to use new krb5_dbe_* tl_data functions. + + * adm.h (struct __krb5_realm_params): add realm_acl_file + Tue Apr 30 17:15:57 1996 Ken Raeburn * configure.in: Invoke AC_C_CROSS before AC_TRY_RUN to pretty up diff --git a/src/include/krb5/adm.h b/src/include/krb5/adm.h index 020282350..b4ec50852 100644 --- a/src/include/krb5/adm.h +++ b/src/include/krb5/adm.h @@ -197,6 +197,7 @@ typedef struct __krb5_realm_params { char * realm_mkey_name; char * realm_stash_file; char * realm_kdc_ports; + char * realm_acl_file; krb5_int32 realm_kadmind_port; krb5_enctype realm_enctype; krb5_deltat realm_max_life; diff --git a/src/include/krb5/kdb.h b/src/include/krb5/kdb.h index 7a8f5bb25..db7157f14 100644 --- a/src/include/krb5/kdb.h +++ b/src/include/krb5/kdb.h @@ -111,18 +111,9 @@ typedef struct _krb5_db_entry_new { #define KRB5_KDB_MAGIC_NUMBER 0xdbdbdbdb #define KRB5_KDB_V1_BASE_LENGTH 38 -typedef struct tl_data_1 { - krb5_timestamp last_pwd_change; /* Last time passwd changed */ -} krb5_tl_last_change; - #define KRB5_TL_LAST_PWD_CHANGE 0x0001 - -typedef struct tl_data_2 { - krb5_timestamp mod_date; - krb5_principal mod_princ; -} krb5_tl_mod_princ; - #define KRB5_TL_MOD_PRINC 0x0002 +#define KRB5_TL_KADM_DATA 0x0003 /* * Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set @@ -263,22 +254,32 @@ krb5_error_code krb5_dbekd_decrypt_key_data krb5_error_code krb5_dbe_create_key_data KRB5_PROTOTYPE((krb5_context, krb5_db_entry *)); -krb5_error_code krb5_dbe_encode_mod_princ_data +krb5_error_code krb5_dbe_update_tl_data KRB5_PROTOTYPE((krb5_context, - krb5_tl_mod_princ *, - krb5_db_entry *)); -krb5_error_code krb5_dbe_decode_mod_princ_data + krb5_db_entry *, + krb5_tl_data *)); +krb5_error_code krb5_dbe_lookup_tl_data KRB5_PROTOTYPE((krb5_context, - krb5_db_entry *, - krb5_tl_mod_princ **)); -krb5_error_code krb5_dbe_encode_last_pwd_change + krb5_db_entry *, + krb5_tl_data *)); +krb5_error_code krb5_dbe_update_last_pwd_change KRB5_PROTOTYPE((krb5_context, - krb5_tl_last_change *, - krb5_db_entry *)); -krb5_error_code krb5_dbe_decode_last_pwd_change + krb5_db_entry *, + krb5_timestamp)); +krb5_error_code krb5_dbe_lookup_last_pwd_change + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_timestamp *)); +krb5_error_code krb5_dbe_update_mod_princ_data KRB5_PROTOTYPE((krb5_context, - krb5_db_entry *, - krb5_tl_last_change *)); + krb5_db_entry *, + krb5_timestamp, + krb5_principal)); +krb5_error_code krb5_dbe_lookup_mod_princ_data + KRB5_PROTOTYPE((krb5_context, + krb5_db_entry *, + krb5_timestamp *, + krb5_principal *)); int krb5_encode_princ_dbmkey KRB5_PROTOTYPE((krb5_context, datum *, @@ -322,11 +323,12 @@ struct __krb5_key_salt_tuple; krb5_error_code krb5_dbe_cpw KRB5_PROTOTYPE((krb5_context, - krb5_encrypt_block *, - struct __krb5_key_salt_tuple *, - int, - char *, - krb5_db_entry *)); + krb5_encrypt_block *, + struct __krb5_key_salt_tuple *, + int, + char *, + int, + krb5_db_entry *)); krb5_error_code krb5_dbe_apw KRB5_PROTOTYPE((krb5_context, krb5_encrypt_block *, diff --git a/src/include/port-sockets.h b/src/include/port-sockets.h new file mode 100644 index 000000000..2c2fba906 --- /dev/null +++ b/src/include/port-sockets.h @@ -0,0 +1,72 @@ +#ifdef _MSDOS + +#include + +/* Some of our own infrastructure where the WinSock stuff was too hairy + to dump into a clean Unix program... */ + +#define SOCKET_INITIALIZE() win_socket_initialize() +#define SOCKET_CLEANUP() WSACleanup() +#define SOCKET_ERRNO (WSAGetLastError()) +#define SOCKET_SET_ERRNO(x) (WSASetLastError (x)) +#define SOCKET_NFDS(f) (0) /* select()'s first arg is ignored */ +#define SOCKET_READ(fd, b, l) (recv(fd, b, l, 0)) +#define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) +#define SOCKET_EINTR WSAEINTR + +int win_socket_initialize(); + +#else /* not _MSDOS */ + +/* If this source file requires it, define struct sockaddr_in + (and possibly other things related to network I/O). */ + +#ifdef HAVE_MACSOCK_H /* Sockets stuff differs on Mac */ +#include "macsock.h" /* Macintosh sockets emulation library */ + +/* Some of our own infrastructure where the WinSock stuff was too hairy + to dump into a clean Unix program... */ + +#define SOCKET_INITIALIZE() (WSAStartup(0x0101, (WSADATA *)0)) +#define SOCKET_CLEANUP() (WSACleanup()) +#define SOCKET_ERRNO (WSAGetLastError()) +#define SOCKET_SET_ERRNO(x) (WSASetLastError(x)) +#define SOCKET_NFDS(f) (0) /* select()'s first arg is ignored */ +#define SOCKET_READ(fd, b, l) (recv(fd, b, l, 0)) +#define SOCKET_WRITE(fd, b, l) (send(fd, b, l, 0)) +#define SOCKET_EINTR WSAEINTR + +#else /* ! HAVE_MACSOCK_H */ /* Sockets stuff for Unix machines */ + +#include /* For struct sockaddr_in and in_addr */ +#include /* For inet_ntoa */ +#include /* For struct hostent, gethostbyname, etc */ +#include /* For MAXHOSTNAMELEN */ +#include /* For SOCK_*, AF_*, etc */ +#include /* For struct timeval */ +#include /* For struct ifconf, for localaddr.c */ + +/* + * Compatability with WinSock calls on MS-Windows... + */ +#define SOCKET unsigned int +#define INVALID_SOCKET ((SOCKET)~0) +#define closesocket close +#define ioctlsocket ioctl +#define SOCKET_ERROR (-1) + +/* Some of our own infrastructure where the WinSock stuff was too hairy + to dump into a clean Unix program... */ + +#define SOCKET_INITIALIZE() (0) /* No error (or anything else) */ +#define SOCKET_CLEANUP() /* nothing */ +#define SOCKET_ERRNO errno +#define SOCKET_SET_ERRNO(x) (errno = (x)) +#define SOCKET_NFDS(f) ((f)+1) /* select() arg for a single fd */ +#define SOCKET_READ read +#define SOCKET_WRITE write +#define SOCKET_EINTR EINTR + +#endif /* HAVE_MACSOCK_H */ + +#endif /* _MSDOS */ diff --git a/src/kadmin/ChangeLog b/src/kadmin/ChangeLog index 4dbf1ae4c..5408cd354 100644 --- a/src/kadmin/ChangeLog +++ b/src/kadmin/ChangeLog @@ -1,3 +1,13 @@ +Fri Jul 12 14:38:30 1996 Marc Horowitz + + * configure.in (CONFIG_DIRS): ktutil is still useful + functionality; add it back to the build. + +Wed Jul 10 16:27:11 1996 Marc Horowitz + + * configure.in: kdbkeys is no longer necessary. + * configure.in (CONFIG_DIRS): added dbutil + Thu Aug 24 19:21:14 1995 Theodore Y. Ts'o * .Sanitize: Add ktutil directory diff --git a/src/kadmin/Makefile.ov b/src/kadmin/Makefile.ov new file mode 100644 index 000000000..1b5cb8e39 --- /dev/null +++ b/src/kadmin/Makefile.ov @@ -0,0 +1,11 @@ +# +# $Id$ +# + +TOP = . +include $(TOP)/config.mk/template + +SUBDIRS = ../lib/kadm5 server create passwd import export v4server \ + keytab cli testing dbutil + +expand SubdirTarget diff --git a/src/kadmin/cli/ChangeLog b/src/kadmin/cli/ChangeLog new file mode 100644 index 000000000..a69639b86 --- /dev/null +++ b/src/kadmin/cli/ChangeLog @@ -0,0 +1,28 @@ +Fri Jul 19 16:10:39 1996 Marc Horowitz + + * ss_wrapper.c (main): ss_execute_line was being called with three + args. There are only two, so no error was ever being returned. + +Thu Jul 18 19:14:51 1996 Marc Horowitz + + * attic/configure.in: removed SS_RULES + + * keytab.c (etype_string): ifdef out des3 reference + + * configure.in: removed SS_RULES + +Mon Jul 15 16:56:43 1996 Barry Jaspan + + * kadmin.1, keytab.c (kadmin_keytab_add): change ktadd usage to + accept -glob + +Tue Jul 9 16:15:46 1996 Marc Horowitz + + * Makefile.in: complete rewrite + * configure.in: add the necessary USE_*_LIBRARY macros + +Mon Jul 8 16:45:20 1996 Barry Jaspan + + * kadmin.1: Update man page for kadm5 changes and functionality. + + diff --git a/src/kadmin/cli/Makefile.in b/src/kadmin/cli/Makefile.in new file mode 100644 index 000000000..c605dd81f --- /dev/null +++ b/src/kadmin/cli/Makefile.in @@ -0,0 +1,19 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadmin +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o + +all:: $(PROG).local $(PROG) + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/cli/Makefile.ov b/src/kadmin/cli/Makefile.ov new file mode 100644 index 000000000..5163bebb8 --- /dev/null +++ b/src/kadmin/cli/Makefile.ov @@ -0,0 +1,28 @@ +TOP = .. +include $(TOP)/config.mk/template + +PROG = kadmin +SRCS = kadmin.c kadmin_ct.c ss_wrapper.c getdate.c keytab.c +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \ + $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB) +DEPENDS = kadmin_ct.c getdate.c + +expand NormalProgram + +PROG = kadmin.local +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKDB5) \ + $(LIBKRB5_ALL) $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) \ + $(BSDLIB) $(NETLIB) $(REGEXLIB) + +expand NormalProgram + +depend:: $(SRCS) +clean:: ; -rm -f getdate.c y.tab.h kadmin_ct.c + +install:: + $(INSTCMD) kadmin.1 $(INSTALL_MANDIR)/cat1/kadmin.1 + +# needed until we run makedepend +kadmin_ct.c: kadmin_ct.ct +kadmin_ct.o: kadmin_ct.c diff --git a/src/kadmin/cli/attic/Makefile b/src/kadmin/cli/attic/Makefile new file mode 100644 index 000000000..79e432fe4 --- /dev/null +++ b/src/kadmin/cli/attic/Makefile @@ -0,0 +1,11 @@ +TOP = ../.. +include $(TOP)/config.mk/template + +SRCS = kadmin.c kadmin_ct.ct ss_wrapper.c getdate.y +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o +PROG = cli_secure_admin + +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \ + $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand NormalProgram diff --git a/src/kadmin/cli/attic/Makefile.in b/src/kadmin/cli/attic/Makefile.in new file mode 100644 index 000000000..160016af9 --- /dev/null +++ b/src/kadmin/cli/attic/Makefile.in @@ -0,0 +1,45 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) $(OVSECINC) +OVSECROOT=/home/tlyu/ovsecure +OVSECSTAGE=/afs/dev.mit.edu/reference/ovsecure/sunos/stage +OVSECINC=-I$(OVSECROOT)/include -I$(OVSECSTAGE)/include +OVSECLIB=-L$(OVSECSTAGE)/lib -lclient -lcommon -lrpclib -ldyn +LDFLAGS = -g +LIBOBJS=@LIBOBJS@ +ISODELIB=$(OVSECROOT)/lib/libisode.a +COMERRLIB=$(OVSECROOT)/lib/libcom_err.a +SSLIB=$(OVSECSTAGE)/lib/libss.a +DBMLIB=$(OVSECROOT)/lib/libdb.a +KDBLIB=$(OVSECROOT)/lib/libkdb5.a + +all:: + +KLIB = $(OVSECROOT)/lib/libgssapi_krb5.a $(OVSECROOT)/lib/libkrb5.a $(OVSECROOT)/lib/libcrypto.a $(ISODELIB) $(SSLIB) $(COMERRLIB) $(DBMLIB) +DEPKLIB = $(TOPLIBD)/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB) + +SRCS = + +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o $(LIBOBJS) + +all:: kadmin +kadmin.o: + $(CC) -c $(CCOPTS) $(OVSECINC) $(DEFS) kadmin.c +getdate.c getdate.h: getdate.y + $(RM) getdate.c getdate.h y.tab.* + $(YACC) -d $(srcdir)/getdate.y + $(MV) y.tab.c getdate.c + $(MV) y.tab.h getdate.h + +kadmin: $(OBJS) + $(CC) -o kadmin $(CFLAGS) $(OBJS) $(OVSECLIB) $(KLIB) $(LIBS) + +# needed until we run makedepend +kadmin_ct.c: kadmin_ct.ct + +kadmin_ct.o: kadmin_ct.c + +clean:: + $(RM) kadmin $(OBJS) kadmin_ct.c getdate.c getdate.h y.tab.c y.tab.h + +# testing rule for building getdate +getdate: getdate.c + $(CC) -o getdate $(CFLAGS) -DTEST getdate.c diff --git a/src/kadmin/cli/attic/configure.in b/src/kadmin/cli/attic/configure.in new file mode 100644 index 000000000..8cca51316 --- /dev/null +++ b/src/kadmin/cli/attic/configure.in @@ -0,0 +1,15 @@ +AC_INIT(getdate.y) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +AC_PROG_YACC +AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h) +AC_HAVE_FUNCS(ftime timezone) +AC_CHECK_LIB(ndbm,main) +AC_CHECK_LIB(dbm,main) +AC_REPLACE_FUNCS([setenv memmove]) +KRB_INCLUDE +ISODE_INCLUDE +WITH_KRB5ROOT +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/cli/attic/getdate.y b/src/kadmin/cli/attic/getdate.y new file mode 100644 index 000000000..6b03e73bb --- /dev/null +++ b/src/kadmin/cli/attic/getdate.y @@ -0,0 +1,1006 @@ +%{ +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +#include +#else +#include "config.h" +#endif +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* The following block of alloca-related preprocessor directives is here + solely to allow compilation by non GNU-C compilers of the C parser + produced from this file by old versions of bison. Newer versions of + bison include a block similar to this one in bison.simple. */ + +#ifdef __GNUC__ +#undef alloca +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include +#else +#ifdef _AIX /* for Bison */ + #pragma alloca +#else +void *alloca (); +#endif +#endif +#endif + +#include +#include + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include if that will be used. */ + +#if defined(vms) + +#include +#include + +#else + +#include + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#endif + +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +#if defined(HAVE_SYS_TIMEB_H) +#include +#else +/* +** We use the obsolete `struct timeb' as part of our interface! +** Since the system doesn't have it, we define it here; +** our callers must do likewise. +*/ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +#endif /* defined(HAVE_SYS_TIMEB_H) */ + +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +extern struct tm *gmtime(); +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yylex (); +static int yyerror (); + +#if !defined(lint) && !defined(SABER) +static char RCS[] = + "$Header$"; +#endif /* !defined(lint) && !defined(SABER) */ + + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm(a, b) + struct tm *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + return + ( + ( + ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (time_t)(ay-by) * 365 + )*24 + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm, gmt; + struct timeb ftz; + time_t Start; + time_t tod; + + yyInput = p; + if (now == NULL) { + now = &ftz; + (void)time(&ftz.time); + + if (! (tm = gmtime (&ftz.time))) + return -1; + gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60; + } + + tm = localtime(&now->time); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/src/kadmin/cli/attic/kadmin.c b/src/kadmin/cli/attic/kadmin.c new file mode 100644 index 000000000..91d2a71e4 --- /dev/null +++ b/src/kadmin/cli/attic/kadmin.c @@ -0,0 +1,958 @@ +/* + * Copyright 1994 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. + * + * kadmin.c: base functions for a kadmin command line interface using + * the OVSecure library + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* special struct to convert flag names for principals + to actual krb5_flags for a principal */ +struct pflag { + char *flagname; /* name of flag as typed to CLI */ + int flaglen; /* length of string (not counting -,+) */ + krb5_flags theflag; /* actual principal flag to set/clear */ + int set; /* 0 means clear, 1 means set (on '-') */ +}; + +static struct pflag flags[] = { +{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, +{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, +{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, +{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, +{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, +{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, +{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, +{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, +{"requres_hwauth", 14, KRB5_KDB_REQUIRES_HW_AUTH, 0}, +{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, +{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, +{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 } +}; + +static char *prflags[] = { + "DISALLOW_POSTDATED", /* 0x00000001 */ + "DISALLOW_FORWARDABLE", /* 0x00000002 */ + "DISALLOW_TGT_BASED", /* 0x00000004 */ + "DISALLOW_RENEWABLE", /* 0x00000008 */ + "DISALLOW_PROXIABLE", /* 0x00000010 */ + "DISALLOW_DUP_SKEY", /* 0x00000020 */ + "DISALLOW_ALL_TIX", /* 0x00000040 */ + "REQUIRES_PRE_AUTH", /* 0x00000080 */ + "REQUIRES_HW_AUTH", /* 0x00000100 */ + "REQUIRES_PWCHANGE", /* 0x00000200 */ + "UNKNOWN_0x00000400", /* 0x00000400 */ + "UNKNOWN_0x00000800", /* 0x00000800 */ + "DISALLOW_SVR", /* 0x00001000 */ + "PWCHANGE_SERVICE" /* 0x00002000 */ +}; + +char *getenv(); +struct passwd *getpwuid(); +int exit_status = 0; +char *def_realm = NULL; + +void *ovsec_hndl = NULL; + +void usage() +{ + fprintf(stderr, + "usage: kadmin [-r realm] [-p principal] [-k keytab] [-q query]\n"); + exit(1); +} + +/* this is a wrapper to go around krb5_parse_principal so we can set + the default realm up properly */ +krb5_error_code kadmin_parse_name(name, principal) + char *name; + krb5_principal *principal; +{ + char *cp, *fullname; + krb5_error_code retval; + + /* assumes def_realm is initialized! */ + fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); + if (fullname == NULL) + return ENOMEM; + strcpy(fullname, name); + cp = strchr(fullname, '@'); + while (cp) { + if (cp - fullname && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '@'); + } + if (cp == NULL) { + strcat(fullname, "@"); + strcat(fullname, def_realm); + } + retval = krb5_parse_name(fullname, principal); + free(fullname); + return retval; +} + +char *kadmin_startup(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + char *princstr = NULL, *keytab = NULL, *query = NULL; + char *luser, *canon, *cp; + int optchar, freeprinc = 0; + struct passwd *pw; + ovsec_kadm_ret_t retval; + krb5_ccache cc; + krb5_principal princ; + + while ((optchar = getopt(argc, argv, "r:p:k:q:")) != EOF) { + switch (optchar) { + case 'r': + def_realm = optarg; + break; + case 'p': + princstr = optarg; + break; + case 'k': + fprintf(stderr, "kadmin: -k not supported yet\n"); + exit(1); + break; + case 'q': + query = optarg; + break; + default: + usage(); + } + } + if (def_realm == NULL && krb5_get_default_realm(&def_realm)) { + if (freeprinc) + free(princstr); + fprintf(stderr, "kadmin: unable to get default realm\n"); + exit(1); + } + if (princstr == NULL) { + if (!krb5_cc_default(&cc) && !krb5_cc_get_principal(cc, &princ)) { + char *realm = NULL; + if (krb5_unparse_name(princ, &canon)) { + fprintf(stderr, + "kadmin: unable to canonicalize principal\n"); + krb5_free_principal(princ); + exit(1); + } + /* strip out realm of principal if it's there */ + realm = strchr(canon, '@'); + while (realm) { + if (realm - canon && *(realm - 1) != '\\') + break; + else + realm = strchr(realm, '@'); + } + if (realm) + *realm++ = '\0'; + cp = strchr(canon, '/'); + while (cp) { + if (cp - canon && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '/'); + } + if (cp != NULL) + *cp = '\0'; + princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ + + (realm ? 1 + strlen(realm) : 0) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, canon); + strcat(princstr, "/admin"); + if (realm) { + strcat(princstr, "@"); + strcat(princstr, realm); + } + free(canon); + krb5_free_principal(princ); + freeprinc++; + } else if (luser = getenv("USER")) { + princstr = malloc(strlen(luser) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, luser); + strcat(princstr, "/admin"); + strcat(princstr, "@"); + strcat(princstr, def_realm); + freeprinc++; + } else if (pw = getpwuid(getuid())) { + princstr = malloc(strlen(pw->pw_name) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, pw->pw_name); + strcat(princstr, "/admin@"); + strcat(princstr, def_realm); + freeprinc++; + } else { + fprintf(stderr, "kadmin: unable to figure out a principal name\n"); + exit(1); + } + } + retval = ovsec_kadm_init_with_password(princstr, NULL, + OVSEC_KADM_ADMIN_SERVICE, + def_realm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_hndl); + if (freeprinc) + free(princstr); + if (retval) { /* assume kadm_init does init_ets() */ + com_err("kadmin", retval, "while initializing kadmin interface"); + exit(1); + } + return query; +} + +int quit() +{ + ovsec_kadm_destroy(ovsec_hndl); + /* insert more random cleanup here */ +} + +void kadmin_delprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_ret_t retval; + krb5_principal princ; + char *canon; + char reply[5]; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "delete_principal: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "delete_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("delete_principal", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("delete_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Principal \"%s\" not deleted\n", canon); + free(canon); + krb5_free_principal(princ); + return; + } + } + retval = ovsec_kadm_delete_principal(ovsec_hndl, princ); + krb5_free_principal(princ); + if (retval) { + com_err("delete_principal", retval, + "while deleteing principal \"%s\"", canon); + free(canon); + return; + } + printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon); + free(canon); + return; +} + +void kadmin_renprinc(argc, argv) + int argc; + char *argv[]; +{ + krb5_principal oldprinc, newprinc; + char *oldcanon, *newcanon; + char reply[5]; + ovsec_kadm_ret_t retval; + + if (argc < 3 || argc > 4) { + fprintf(stderr, "rename_principal: wrong number of arguments\n"); + return; + } + if (argc == 4 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "rename_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 2], &oldprinc); + if (retval) { + com_err("rename_principal", retval, "while parsing old principal"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &newprinc); + if (retval) { + krb5_free_principal(oldprinc); + com_err("rename_principal", retval, "while parsing new principal"); + return; + } + retval = krb5_unparse_name(oldprinc, &oldcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing old principal"); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + retval = krb5_unparse_name(newprinc, &newcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing new principal"); + free(oldcanon); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + if (argc == 3) { + printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ", + oldcanon, newcanon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, + "rename_principal: \"%s\" NOT renamed to \"%s\".\n", + oldcanon, newcanon); + free(newcanon); + free(oldcanon); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + } + retval = ovsec_kadm_rename_principal(ovsec_hndl, oldprinc, newprinc); + krb5_free_principal(oldprinc); + krb5_free_principal(newprinc); + if (retval) { + com_err("rename_principal", retval, + "while renaming \"%s\" to \"%s\".", oldcanon, + newcanon); + free(newcanon); + free(oldcanon); + return; + } + printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n", + oldcanon, newcanon, newcanon); + return; +} + +void kadmin_cpw(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_ret_t retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + char *canon; + krb5_principal princ; + + if (argc < 2 || argc > 4) { + fprintf(stderr, "change_password: too many arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("change_password", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("change_password", retval, "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) { + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, argv[2]); + krb5_free_principal(princ); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else if ((argc == 3) && (strlen(argv[1]) == 8) && + !strcmp("-randkey", argv[1])) { + krb5_keyblock *newkey = NULL; + retval = ovsec_kadm_randkey_principal(ovsec_hndl, princ, &newkey); + krb5_free_principal(princ); + if (retval) { + com_err("change_password", retval, + "while randomizing key for \"%s\".", canon); + free(canon); + return; + } + memset(newkey->contents, 0, newkey->length); + printf("Key for \"%s\" randomized.\n", canon); + free(canon); + return; + } else if (argc == 2) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("change_password", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(princ); + return; + } + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, newpw); + krb5_free_principal(princ); + memset(newpw, 0, sizeof (newpw)); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } + fprintf(stderr, "change_password: bad arguments\n"); + free(canon); + krb5_free_principal(princ); + return; +} + +int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) + int argc; + char *argv[]; + ovsec_kadm_principal_ent_t oprinc; + u_int32 *mask; + char **pass, *caller; +{ + int i, j; + struct timeb now; + krb5_error_code retval; + + *mask = 0; + *pass = NULL; + ftime(&now); + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 7 && + !strcmp("-expire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->princ_expire_time = get_date(argv[i], now); + *mask |= OVSEC_KADM_PRINC_EXPIRE_TIME; + continue; + } + } + if (strlen(argv[i]) == 9 && + !strcmp("-pwexpire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->pw_expiration = get_date(argv[i], now); + *mask |= OVSEC_KADM_PW_EXPIRATION; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-maxlife", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->max_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_MAX_LIFE; + continue; + } + } + if (strlen(argv[i]) == 5 && + !strcmp("-kvno", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->kvno = atoi(argv[i]); + *mask |= OVSEC_KADM_KVNO; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-policy", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->policy = argv[i]; + *mask |= OVSEC_KADM_POLICY; + continue; + } + } + if (strlen(argv[i]) == 12 && + !strcmp("-clearpolicy", argv[i])) { + oprinc->policy = NULL; + *mask |= OVSEC_KADM_POLICY_CLR; + continue; + } + if (strlen(argv[i]) == 3 && + !strcmp("-pw", argv[i])) { + if (++i > argc - 2) + return -1; + else { + *pass = argv[i]; + continue; + } + } + for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { + if (strlen(argv[i]) == flags[j].flaglen + 1 && + !strcmp(flags[j].flagname, + &argv[i][1] /* strip off leading + or - */)) { + if (flags[j].set && argv[i][0] == '-' || + !flags[j].set && argv[i][0] == '+') { + oprinc->attributes |= flags[j].theflag; + *mask |= OVSEC_KADM_ATTRIBUTES; + break; + } else if (flags[j].set && argv[i][0] == '+' || + !flags[j].set && argv[i][0] == '-') { + oprinc->attributes &= ~flags[j].theflag; + *mask |= OVSEC_KADM_ATTRIBUTES; + break; + } else { + return -1; + } + } + } + return -1; + } + if (i != argc - 1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } + retval = kadmin_parse_name(argv[i], &oprinc->principal); + if (retval) { + com_err(caller, retval, "while parsing principal"); + return -1; + } + return 0; +} + +void kadmin_addprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_rec princ; + u_int32 mask; + char *pass, *canon; + krb5_error_code retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, "add_principal")) { + fprintf(stderr, "add_principal: bad arguments\n"); + return; + } + retval = krb5_unparse_name(princ.principal, &canon); + if (retval) { + com_err("add_principal", + retval, "while canonicalizing principal"); + krb5_free_principal(princ.principal); + return; + } + if (pass == NULL) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("add_principal", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(princ.principal); + return; + } + pass = newpw; + } + mask |= OVSEC_KADM_PRINCIPAL; + retval = ovsec_kadm_create_principal(ovsec_hndl, &princ, mask, pass); + krb5_free_principal(princ.principal); + if (retval) { + com_err("add_principal", retval, "while creating \"%s\".", + canon); + free(canon); + return; + } + printf("Principal \"%s\" created.\n", canon); + free(canon); +} + +void kadmin_modprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_rec princ; + u_int32 mask; + krb5_error_code retval; + char *pass, *canon; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, "modify_principal")) { + fprintf(stderr, "modify_principal: bad arguments\n"); + return; + } + retval = krb5_unparse_name(princ.principal, &canon); + if (retval) { + com_err("modify_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(princ.principal); + return; + } + retval = ovsec_kadm_modify_principal(ovsec_hndl, &princ, mask); + if (retval) { + com_err("modify_principal", retval, "while modifying \"%s\".", + argv[argc - 1]); + return; + } +} + +void kadmin_getprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_t dprinc; + krb5_principal princ; + krb5_error_code retval; + char *canon, *modcanon; + int i; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "get_principal: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) { + fprintf(stderr, "get_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("get_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("get_principal", retval, "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + retval = ovsec_kadm_get_principal(ovsec_hndl, princ, &dprinc); + krb5_free_principal(princ); + if (retval) { + com_err("get_principal", retval, "while retrieving \"%s\".", canon); + free(canon); + return; + } + retval = krb5_unparse_name(dprinc->mod_name, &modcanon); + if (retval) { + com_err("get_principal", retval, "while unparsing modname"); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); + free(canon); + return; + } + if (argc == 2) { + printf("Principal: %s\n", canon); + printf("Expiration date: %d\n", dprinc->princ_expire_time); + printf("Last password change: %d\n", dprinc->last_pwd_change); + printf("Password expiration date: %d\n", dprinc->pw_expiration); + printf("Maximum life: %d\n", dprinc->max_life); + printf("Last modified: by %s\n\ton %d\n", + modcanon, dprinc->mod_date); + printf("Attributes: "); + for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { + if (dprinc->attributes & (krb5_flags) 1 << i) + printf(" %s", prflags[i]); + } + printf("\n"); + printf("Key version: %d\n", dprinc->kvno); + printf("Master key version: %d\n", dprinc->mkvno); + printf("Policy: %s\n", dprinc->policy); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\n", + canon, dprinc->princ_expire_time, dprinc->last_pwd_change, + dprinc->pw_expiration, dprinc->max_life, modcanon, + dprinc->mod_date, dprinc->attributes, dprinc->kvno, + dprinc->mkvno, dprinc->policy); + } + free(modcanon); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); + free(canon); +} + +int kadmin_parse_policy_args(argc, argv, policy, mask, caller) + int argc; + char *argv[]; + ovsec_kadm_policy_ent_t policy; + u_int32 *mask; + char *caller; +{ + int i; + struct timeb now; + krb5_error_code retval; + + ftime(&now); + *mask = 0; + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-maxlife")) { + if (++i > argc -2) + return -1; + else { + policy->pw_max_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_PW_MAX_LIFE; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-minlife")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_PW_MIN_LIFE; + continue; + } + } else if (strlen(argv[i]) == 10 && + !strcmp(argv[i], "-minlength")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_length = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_MIN_LENGTH; + continue; + } + } else if (strlen(argv[i]) == 11 && + !strcmp(argv[i], "-minclasses")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_classes = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_MIN_CLASSES; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-history")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_history_num = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_HISTORY_NUM; + continue; + } + } else + return -1; + } + if (i != argc -1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } else + return 0; +} + +void kadmin_addpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + u_int32 mask; + ovsec_kadm_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { + fprintf(stderr, "add_policy: bad arguments\n"); + return; + } else { + policy.policy = argv[argc - 1]; + mask |= OVSEC_KADM_POLICY; + retval = ovsec_kadm_create_policy(ovsec_hndl, &policy, mask); + if (retval) { + com_err("add_policy", retval, "while creating policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_modpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + u_int32 mask; + ovsec_kadm_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, + "modify_policy")) { + fprintf(stderr, "modify_policy: bad arguments\n"); + return; + } else { + policy.policy = argv[argc - 1]; + retval = ovsec_kadm_modify_policy(ovsec_hndl, &policy, mask); + if (retval) { + com_err("modify_policy", retval, "while modifying policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_delpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char reply[5]; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "delete_policy: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "delete_policy: bad arguments\n"); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]); + return; + } + } + retval = ovsec_kadm_delete_policy(ovsec_hndl, argv[argc - 1]); + if (retval) { + com_err("delete_policy:", retval, "while deleting policy \"%s\"", + argv[argc - 1]); + return; + } + return; +} + +void kadmin_getpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + ovsec_kadm_policy_ent_t policy; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "get_policy: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) { + fprintf(stderr, "get_policy: bad arguments\n"); + return; + } + retval = ovsec_kadm_get_policy(ovsec_hndl, argv[argc - 1], &policy); + if (retval) { + com_err("get_policy", retval, "while retrieving policy \"%s\".", + argv[argc - 1]); + return; + } + if (argc == 2) { + printf("Policy: %s\n", policy->policy); + printf("Maximum password life: %d\n", policy->pw_max_life); + printf("Minimum password life: %d\n", policy->pw_min_life); + printf("Minimum password length: %d\n", policy->pw_min_length); + printf("Minimum number of password character classes: %d\n", + policy->pw_min_classes); + printf("Number of old keys kept: %d\n", policy->pw_history_num); + printf("Reference count: %d\n", policy->policy_refcnt); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n", + policy->policy, policy->pw_max_life, policy->pw_min_life, + policy->pw_min_length, policy->pw_min_classes, + policy->pw_history_num, policy->policy_refcnt); + } + ovsec_kadm_free_policy_ent(ovsec_hndl, policy); + return; +} + +kadmin_getprivs(argc, argv) + int argc; + char *argv[]; +{ + static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"}; + krb5_error_code retval; + int i; + u_int32 plist; + + if (argc != 1) { + fprintf(stderr, "get_privs: bad arguments\n"); + return; + } + retval = ovsec_kadm_get_privs(ovsec_hndl, &plist); + if (retval) { + com_err("get_privs", retval, "while retrieving privileges"); + return; + } + printf("current privileges:"); + for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { + if (plist & 1 << i) + printf(" %s", privs[i]); + } + printf("\n"); + return; +} diff --git a/src/kadmin/cli/attic/kadmin_ct.ct b/src/kadmin/cli/attic/kadmin_ct.ct new file mode 100644 index 000000000..f5a67ed53 --- /dev/null +++ b/src/kadmin/cli/attic/kadmin_ct.ct @@ -0,0 +1,67 @@ +# Copyright 1994 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. +# +# +# Command table for kadmin CLI for OVSecure +# + +command_table kadmin_cmds; + +request kadmin_addprinc, "Add principal", + add_prinicpal, addprinc, ank; + +request kadmin_delprinc, "Delete principal", + delete_principal, delprinc; + +request kadmin_modprinc, "Modify principal", + modify_principal, modprinc; + +request kadmin_renprinc, "Rename principal", + rename_principal, renprinc; + +request kadmin_cpw, "Change password", + change_password, cpw; + +request kadmin_getprinc, "Get principal", + get_principal, getprinc; + +request kadmin_addpol, "Add policy", + add_policy, addpol; + +request kadmin_modpol, "Modify policy", + modify_policy, modpol; + +request kadmin_delpol, "Delete policy", + delete_policy, delpol; + +request kadmin_getpol, "Get policy", + get_policy, getpol; + +request kadmin_getprivs, "Get privileges", + get_privs, getprivs; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; + diff --git a/src/kadmin/cli/attic/memmove.c b/src/kadmin/cli/attic/memmove.c new file mode 100644 index 000000000..abc91e923 --- /dev/null +++ b/src/kadmin/cli/attic/memmove.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#define MEMMOVE + +/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */ + +#include +#include +#ifdef USE_STRING_H +#include +#else +#include +#endif + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/src/kadmin/cli/attic/setenv.c b/src/kadmin/cli/attic/setenv.c new file mode 100644 index 000000000..a2432c3d6 --- /dev/null +++ b/src/kadmin/cli/attic/setenv.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */ +/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */ + +#ifndef __STDC__ +#define const +#endif + +#include +#include +#include + +#ifndef __P +#define __P(x) () +#endif +char *__findenv __P((const char *, int *)); + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *c; + int l_value, offset; + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((c = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(c) >= l_value) { /* old larger; copy over */ + while (*c++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **p; + + for (p = environ, cnt = 0; *p; ++p, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2))); + if (!p) + return (-1); + memcpy(p, environ, cnt * sizeof(char *)); + environ = p; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(c - name) + l_value + 2)))) + return (-1); + for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); + for (*c++ = '='; *c++ = *value++;); + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **p; + int offset; + + while (__findenv(name, &offset)) /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; +} + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + + return (__findenv(name, &offset)); +} + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + */ +static char * +__findenv(name, offset) + register const char *name; + int *offset; +{ + extern char **environ; + register int len; + register const char *np; + register char **p, *c; + + if (name == NULL || environ == NULL) + return (NULL); + for (np = name; *np && *np != '='; ++np) + continue; + len = np - name; + for (p = environ; (c = *p) != NULL; ++p) + if (strncmp(c, name, len) == 0 && c[len] == '=') { + *offset = p - environ; + return (c + len + 1); + } + return (NULL); +} diff --git a/src/kadmin/cli/attic/ss_wrapper.c b/src/kadmin/cli/attic/ss_wrapper.c new file mode 100644 index 000000000..f7bbda516 --- /dev/null +++ b/src/kadmin/cli/attic/ss_wrapper.c @@ -0,0 +1,56 @@ +/* + * Copyright 1994 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. + * + * + * ss wrapper for kadmin + */ + +#include +#include +#include + +extern ss_request_table kadmin_cmds; +extern int exit_status; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + request = kadmin_startup(argc, argv); + sci_idx = ss_create_invocation("kadmin", "5.0", (char *) NULL, + &kadmin_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + if (request) { + (void) ss_execute_line(sci_idx, request, &code); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/cli/configure.in b/src/kadmin/cli/configure.in new file mode 100644 index 000000000..713d7d212 --- /dev/null +++ b/src/kadmin/cli/configure.in @@ -0,0 +1,20 @@ +AC_INIT(getdate.y) +WITH_CCOPTS +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_YACC +AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h) +AC_HAVE_FUNCS(ftime timezone) +AC_CHECK_LIB(ndbm,main) +AC_CHECK_LIB(dbm,main) +AC_REPLACE_FUNCS([setenv memmove strftime]) +KRB_INCLUDE +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +USE_SS_LIBRARY +KRB5_LIBRARIES +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/cli/dump.c b/src/kadmin/cli/dump.c new file mode 100644 index 000000000..2c5e4e753 --- /dev/null +++ b/src/kadmin/cli/dump.c @@ -0,0 +1,1485 @@ +/* + * admin/edit/dump.c + * + * Copyright 1990,1991 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. + * + * Dump a KDC database. This file was originally written to be part + * of kdb5_edit but has now been adapted for kadmin. + */ + +#include +#include +#include + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context context; + int verbose; +}; + +/* External data */ +extern int exit_status; +extern krb5_context context; +extern void *handle; + +/* Strings */ + +static const char k5beta5_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char kadm5_dump_header[] = "kadm5 load_dump version 4.0\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta5_fmt_name[] = "Kerberos version 5 old format"; +static const char k5beta6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname\n"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; + +static const char oldoption[] = "-old"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char dump_tmptrail[] = "~"; + +/* Can't use krb5_dbe_find_enctype because we have a */ +/* kadm5_principal_ent_t and not a krb5_db_entry */ +static krb5_error_code +find_enctype(dbentp, enctype, salttype, kentp) + kadm5_principal_ent_rec *dbentp; + krb5_enctype enctype; + krb5_int32 salttype; + krb5_key_data **kentp; +{ + int i; + int maxkvno; + krb5_key_data *datap; + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i=0; in_key_data; i++) { + if ((dbentp->key_data[i].key_data_type[0] == enctype) && + ((dbentp->key_data[i].key_data_type[1] == salttype) || + (salttype < 0))) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + } + } + if (maxkvno >= 0) { + *kentp = datap; + return(0); + } + return(ENOENT); +} + + +/* + * dump_k5beta5_header() - Make a dump header that is recognizable by Kerberos + * Version 5 Beta 5 and previous releases. + */ +static krb5_error_code +dump_k5beta5_header(arglist) + struct dump_args *arglist; +{ + /* The old header consists of the leading string */ + fprintf(arglist->ofile, k5beta5_dump_header); + return(0); +} + + +/* + * dump_k5beta5_iterator() - Dump an entry in a format that is usable + * by Kerberos Version 5 Beta 5 and previous + * releases. + */ +static krb5_error_code +dump_k5beta5_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *mod_name; + krb5_tl_data *pwchg; + krb5_key_data *pkey, *akey, nullkey; + int i; + + /* Initialize */ + arg = (struct dump_args *) ptr; + mod_name = (char *) NULL; + memset(&nullkey, 0, sizeof(nullkey)); + + /* + * Deserialize the modifier record. + */ + mod_name = (char *) NULL; + pkey = akey = (krb5_key_data *) NULL; + + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->context, + entry->mod_name, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + + /* + * Find the 'primary' key and the 'alternate' key. + */ + if ((retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_NORMAL, + &pkey)) && + (retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + &akey))) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + return(retval); + } + + /* If we only have one type, then ship it out as the primary. */ + if (!pkey && akey) { + pkey = akey; + akey = &nullkey; + } + else { + if (!akey) + akey = &nullkey; + } + + /* + * First put out strings representing the length of the variable + * length data in this record, then the name and the primary key type. + */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), + strlen(mod_name), + (krb5_int32) pkey->key_data_length[0], + (krb5_int32) akey->key_data_length[0], + (krb5_int32) pkey->key_data_length[1], + (krb5_int32) akey->key_data_length[1], + name, + (krb5_int32) pkey->key_data_type[0]); + for (i=0; ikey_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); + } + /* + * Second, print out strings representing the standard integer + * data in this record. + */ + fprintf(arg->ofile, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", + (krb5_int32) pkey->key_data_kvno, + entry->max_life, entry->max_renewable_life, + 1 /* Fake mkvno */, entry->princ_expire_time, entry->pw_expiration, + entry->last_pwd_change, entry->last_success, entry->last_failed, + entry->fail_auth_count, mod_name, entry->mod_date, + entry->attributes, pkey->key_data_type[1]); + + /* Pound out the salt data, if present. */ + for (i=0; ikey_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); + } + /* Pound out the alternate key type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); + for (i=0; ikey_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); + } + /* Pound out the alternate salt type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); + for (i=0; ikey_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); + } + /* Pound out the expansion data. (is null) */ + for (i=0; i < 8; i++) { + fprintf(arg->ofile, "\t%u", 0); + } + fprintf(arg->ofile, ";\n"); + /* If we're blabbing, do it */ + if (arg->verbose) + fprintf(stderr, "%s\n", name); + krb5_xfree(mod_name); + + return(0); +} + + +/* + * dump_k5beta6_header() - Output the k5beta6 dump header. + */ +static krb5_error_code +dump_k5beta6_header(arglist) + struct dump_args *arglist; +{ + /* The k5beta6 header consists of the leading string */ + fprintf(arglist->ofile, k5_dump_header); + return(0); +} + + +/* + * dump_k5beta6_iterator() - Output a dump record in k5beta6 format. + */ +static krb5_error_code +dump_k5beta6_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval = 0; + struct dump_args *arg; + krb5_tl_data *tlp, *etl; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length ] + * n_key_data*[ver kvno ver*(type length )] + * + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct, and find + * E_DATA while we're at it. + */ + counter = 0; + etl = NULL; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + etl = tlp; + counter++; + } + + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + KRB5_KDB_V1_BASE_LENGTH + (etl ? etl->tl_data_length : 0), + strlen(name), + (int) entry->n_tl_data, + (int) entry->n_key_data, + etl ? etl->tl_data_length : 0, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->princ_expire_time, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + /* skip E_DATA since it is included later */ + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + continue; + + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; itl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; countern_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; ikey_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; jkey_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (etl && etl->tl_data_length) + for (i=0; itl_data_length; i++) + fprintf(arg->ofile, "%02x", etl->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + fprintf(stderr, "%s\n", name); + } + else { + fprintf(stderr, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + return(retval); +} + + +/* + * usage is: + * dump_db [-old] [-verbose] [filename|- [principals...]] + */ +void dump_db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_args arglist; + int error; + char *programname; + char *ofile; + krb5_error_code kret; + krb5_error_code (*dump_iterator) PROTOTYPE((krb5_pointer, + char *, + kadm5_principal_ent_rec *)); + krb5_error_code (*dump_header) PROTOTYPE((struct dump_args *)); + const char * dump_name; + int aindex, num, i; + krb5_boolean locked; + char **princs; + kadm5_principal_ent_rec princ_ent; + krb5_principal princ; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + ofile = (char *) NULL; + error = 0; + dump_iterator = dump_k5beta6_iterator; + dump_header = dump_k5beta6_header; + dump_name = k5beta6_fmt_name; + arglist.verbose = 0; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + dump_iterator = dump_k5beta5_iterator; + dump_header = dump_k5beta5_header; + dump_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + arglist.verbose++; + } + else + break; + } + + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + } + + /* this works because of the way aindex and argc are used below */ + if (aindex == argc) { + argv[aindex] = "*"; + argc++; + } + + locked = 0; + if (ofile) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependencies than I + * want to get into. + */ + unlink(ofile); + if (!(f = fopen(ofile, "w"))) { + fprintf(stderr, ofopen_error, + programname, ofile, error_message(errno)); + exit_status++; + goto cleanup; + } + if ((kret = krb5_lock_file(context, + fileno(f), + KRB5_LOCKMODE_EXCLUSIVE))) { + fprintf(stderr, oflock_error, + programname, ofile, error_message(kret)); + exit_status++; + goto cleanup; + } + else + locked = 1; + } else { + f = stdout; + } + + arglist.programname = programname; + arglist.ofile = f; + arglist.context = context; + + if (kret = (*dump_header)(&arglist)) { + fprintf(stderr, dumphdr_err, + programname, dump_name, error_message(kret)); + exit_status++; + goto cleanup; + } + + while (aindex < argc) { + if (kret = kadm5_get_principals(handle, argv[aindex], + &princs, &num)) { + fprintf(stderr, "%s: error retrieving principals " + "matching %s: (%s)\n", programname, + argv[aindex], error_message(kret)); + exit_status++; + goto cleanup; + } + + for (i = 0; i < num; i++) { + if (kret = krb5_parse_name(context, princs[i], + &princ)) { + com_err(programname, kret, + "while parsing principal name"); + exit_status++; + break; + } + if (kret = kadm5_get_principal(handle, princ, + &princ_ent, + KADM5_PRINCIPAL_NORMAL_MASK | + KADM5_KEY_DATA|KADM5_TL_DATA)){ + com_err(programname, kret, + "while retrieving principal entry"); + krb5_free_principal(context, princ); + exit_status++; + break; + } + if (kret = (*dump_iterator)(&arglist, princs[i], &princ_ent)) { + exit_status++; + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + break; + } + + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + } + + kadm5_free_name_list(handle, princs, num); + aindex++; + if (kret) + goto cleanup; + } + +cleanup: + if (ofile) + fclose(f); + + if (locked) + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); +} + + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; ikey_data_length[0] = key_len; + akey->key_data_length[0] = alt_key_len; + pkey->key_data_length[1] = salt_len; + akey->key_data_length[1] = alt_salt_len; + name = (char *) NULL; + mod_name = (char *) NULL; + /* + * Get the memory for the variable length fields. + */ + if ((name = (char *) malloc((size_t) (name_len + 1))) && + (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && + (!key_len || + (pkey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (key_len + 1)))) && + (!alt_key_len || + (akey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && + (!salt_len || + (pkey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (salt_len + 1)))) && + (!alt_salt_len || + (akey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) + ) { + error = 0; + + /* Read the principal name */ + if (read_string(filep, name, name_len, linenop)) { + try2read = read_name_string; + error++; + } + /* Read the key type */ + if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { + try2read = read_key_type; + error++; + } + pkey->key_data_type[0] = tmpint1; + /* Read the old format key */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[0], + pkey->key_data_length[0])) { + try2read = read_key_data; + error++; + } + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((pkey->key_data_length[0] > 4) + && (pkey->key_data_contents[0][0] == 0) + && (pkey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = pkey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = pkey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + pkey->key_data_length[0] = shortlen; + pkey->key_data_contents[0] = shortcopy; + } + + /* Read principal attributes */ + if (!error && (fscanf(filep, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", + &tmpint1, &dbent.max_life, + &dbent.max_renewable_life, + &tmpint2, &dbent.expiration, + &dbent.pw_expiration, &last_pwd_change, + &dbent.last_success, &dbent.last_failed, + &tmpint3) != 10)) { + try2read = read_pr_data1; + error++; + } + pkey->key_data_kvno = tmpint1; + dbent.fail_auth_count = tmpint3; + /* Read modifier name */ + if (!error && read_string(filep, + mod_name, + mod_name_len, + linenop)) { + try2read = read_mod_name; + error++; + } + /* Read second set of attributes */ + if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", + &mod_date, &dbent.attributes, + &tmpint1) != 3)) { + try2read = read_pr_data2; + error++; + } + pkey->key_data_type[1] = tmpint1; + /* Read salt data */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[1], + pkey->key_data_length[1])) { + try2read = read_salt_data; + error++; + } + /* Read alternate key type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_akey_type; + error++; + } + akey->key_data_type[0] = tmpint1; + /* Read alternate key */ + if (!error && read_octet_string(filep, + akey->key_data_contents[0], + akey->key_data_length[0])) { + try2read = read_akey_data; + error++; + } + + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((akey->key_data_length[0] > 4) + && (akey->key_data_contents[0][0] == 0) + && (akey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = akey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = akey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + akey->key_data_length[0] = shortlen; + akey->key_data_contents[0] = shortcopy; + } + + /* Read alternate salt type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_asalt_type; + error++; + } + akey->key_data_type[1] = tmpint1; + /* Read alternate salt data */ + if (!error && read_octet_string(filep, + akey->key_data_contents[1], + akey->key_data_length[1])) { + try2read = read_asalt_data; + error++; + } + /* Read expansion data - discard it */ + if (!error) { + for (i=0; i<8; i++) { + if (fscanf(filep, "\t%u", &tmpint1) != 1) { + try2read = read_exp_data; + error++; + break; + } + } + if (!error) + find_record_end(filep, fname, *linenop); + } + + /* + * If no error, then we're done reading. Now parse the names + * and store the database dbent. + */ + if (!error) { + if (!(kret = krb5_parse_name(context, + name, + &dbent.princ))) { + if (!(kret = krb5_parse_name(context, + mod_name, + &mod_princ))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(context, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(context, + &dbent, + last_pwd_change))) { + int one = 1; + + dbent.len = KRB5_KDB_V1_BASE_LENGTH; + pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? + 2 : 1; + akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? + 2 : 1; + if ((pkey->key_data_type[0] == + akey->key_data_type[0]) && + (pkey->key_data_type[1] == + akey->key_data_type[1])) + dbent.n_key_data--; + else if ((akey->key_data_type[0] == 0) + && (akey->key_data_length[0] == 0) + && (akey->key_data_type[1] == 0) + && (akey->key_data_length[1] == 0)) + dbent.n_key_data--; + if ((kret = krb5_db_put_principal(context, + &dbent, + &one)) || + (one != 1)) { + fprintf(stderr, store_err_fmt, + fname, *linenop, name, + error_message(kret)); + error++; + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + dbent.n_key_data = 2; + } + krb5_free_principal(context, mod_princ); + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, mod_name, + error_message(kret)); + error++; + } + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + error++; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + + krb5_db_free_principal(context, &dbent, 1); + if (mod_name) + free(mod_name); + if (name) + free(name); + } + else { + if (nmatched != EOF) + fprintf(stderr, rhead_err_fmt, fname, *linenop); + else + retval = -1; + } + return(retval); +} + + +/* + * process_k5_record() - Handle a dump record in new format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5_record(fname, context, filep, verbose, linenop) + char *fname; + krb5_context context; + FILE *filep; + int verbose; + int *linenop; +{ + int retval; + krb5_db_entry dbentry; + krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; + int nread; + int error; + int i, j, one; + char *name; + krb5_key_data *kp, *kdatap; + krb5_tl_data **tlp, *tl; + krb5_octet *op; + krb5_error_code kret; + const char *try2read; + + try2read = (char *) NULL; + memset((char *) &dbentry, 0, sizeof(dbentry)); + (*linenop)++; + retval = 1; + name = (char *) NULL; + kp = (krb5_key_data *) NULL; + op = (krb5_octet *) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); + if (nread == 5) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + /* Get memory for and form tagged data linked list */ + tlp = &dbentry.tl_data; + for (i=0; itl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(context, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* Get the tagged data */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (ikey_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; jkey_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(context, + &dbentry, + &one))) { + fprintf(stderr, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + } + else { + fprintf(stderr, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(context, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + + +/* + * restore_k5beta5_compat() - Restore the database from a K5 Beta + * format dump file. + */ +static int +restore_k5beta5_compat(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5beta5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5beta5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5beta5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + + +/* + * restore_dump() - Restore the database from a standard dump file. + */ +static int +restore_dump(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + +/* + * Usage is + * load_db [-old] [-verbose] [-update] filename dbname + */ +void +load_db(argc, argv) + int argc; + char **argv; +{ + krb5_error_code kret; + krb5_context context; + FILE *f; + extern char *optarg; + extern int optind; + const char *programname; + const char *dumpfile; + char *dbname; + char *dbname_tmp; + int (*restore_function) PROTOTYPE((const char *, + krb5_context, + const char *, + FILE *, + int)); + const char * restore_name; + int update, verbose; + int aindex; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + dumpfile = (char *) NULL; + dbname = (char *) NULL; + restore_function = restore_dump; + restore_name = standard_fmt_name; + update = 0; + verbose = 0; + exit_status = 0; + dbname_tmp = (char *) NULL; + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + restore_function = restore_k5beta5_compat; + restore_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + verbose = 1; + } + else if (!strcmp(argv[aindex], updateoption)) { + update = 1; + } + else + break; + } + if ((argc - aindex) != 2) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + + dumpfile = argv[aindex]; + dbname = argv[aindex+1]; + if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ + strlen(dump_tmptrail)+1))) { + fprintf(stderr, no_name_mem_fmt, argv[0]); + exit_status++; + return; + } + strcpy(dbname_tmp, dbname); + strcat(dbname_tmp, dump_tmptrail); + + /* + * Initialize the Kerberos context and error tables. + */ + if ((kret = krb5_init_context(&context))) { + fprintf(stderr, ctx_err_fmt, programname); + free(dbname_tmp); + exit_status++; + return; + } + krb5_init_ets(context); + + /* + * Open the dumpfile + */ + if (dumpfile) { + if ((f = fopen(dumpfile, "r+"))) { + kret = krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_SHARED); + } + } + else { + f = stdin; + } + if (f && !kret) { + /* + * Create the new database if not an update restoration. + */ + if (update || !(kret = krb5_db_create(context, dbname_tmp))) { + /* + * Point ourselves at it. + */ + if (!(kret = krb5_db_set_name(context, + (update) ? dbname : dbname_tmp))) { + /* + * Initialize the database. + */ + if (!(kret = krb5_db_init(context))) { + if ((*restore_function)(programname, + context, + (dumpfile) ? dumpfile : stdin_name, + f, + verbose)) { + fprintf(stderr, restfail_fmt, + programname, restore_name); + exit_status++; + } + if ((kret = krb5_db_fini(context))) { + fprintf(stderr, close_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbinit_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbname_err_fmt, + programname, + (update) ? dbname : dbname_tmp, error_message(kret)); + exit_status++; + } + /* + * If there was an error and this is not an update, then + * destroy the database. + */ + if (!update) { + if (exit_status) { + if ((kret = kdb5_db_destroy(context, dbname))) { + fprintf(stderr, dbdelerr_fmt, + programname, dbname_tmp, error_message(kret)); + exit_status++; + } + } + else { + if ((kret = krb5_db_rename(context, + dbname_tmp, + dbname))) { + fprintf(stderr, dbrenerr_fmt, + programname, dbname_tmp, dbname, + error_message(kret)); + exit_status++; + } + } + } + } + else { + fprintf(stderr, dbcreaterr_fmt, + programname, dbname, error_message(kret)); + exit_status++; + } + if (dumpfile) { + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); + fclose(f); + } + } + else { + fprintf(stderr, dfile_err_fmt, dumpfile, error_message(errno)); + exit_status++; + } + free(dbname_tmp); + krb5_free_context(context); +} +#endif diff --git a/src/kadmin/cli/getdate.y b/src/kadmin/cli/getdate.y new file mode 100644 index 000000000..975a819f5 --- /dev/null +++ b/src/kadmin/cli/getdate.y @@ -0,0 +1,1009 @@ +%{ +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +#include +#else +#include "config.h" +#endif +#endif +#include + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* The following block of alloca-related preprocessor directives is here + solely to allow compilation by non GNU-C compilers of the C parser + produced from this file by old versions of bison. Newer versions of + bison include a block similar to this one in bison.simple. */ + +#ifdef __GNUC__ +#undef alloca +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include +#else +#ifdef _AIX /* for Bison */ + #pragma alloca +#else +void *alloca (); +#endif +#endif +#endif + +#include +#include + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include if that will be used. */ + +#if defined(vms) + +#include +#include + +#else + +#include + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#endif + +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +#if defined(HAVE_SYS_TIMEB_H) +#include +#else +/* +** We use the obsolete `struct timeb' as part of our interface! +** Since the system doesn't have it, we define it here; +** our callers must do likewise. +*/ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +#endif /* defined(HAVE_SYS_TIMEB_H) */ + +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +extern struct tm *gmtime(); +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yylex (); +static int yyerror (); + +#if !defined(lint) && !defined(SABER) +static char RCS[] = + "$Header$"; +#endif /* !defined(lint) && !defined(SABER) */ + + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ + { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "kdt", tZONE, -HOUR(10) }, /* Korean Daylight */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm(a, b) + struct tm *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + return + ( + ( + ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (time_t)(ay-by) * 365 + )*24 + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm, gmt; + struct timeb ftz; + time_t Start; + time_t tod; + + yyInput = p; + if (now == NULL) { + now = &ftz; + (void)time(&ftz.time); + + if (! (tm = gmtime (&ftz.time))) + return -1; + gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60; + } + + tm = localtime(&now->time); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/src/kadmin/cli/kadmin.1 b/src/kadmin/cli/kadmin.1 new file mode 100644 index 000000000..a8db58334 --- /dev/null +++ b/src/kadmin/cli/kadmin.1 @@ -0,0 +1,473 @@ +KADMIN(8) USER_COMMANDS KADMIN(8) + +NAME + kadmin - a command line interface to the Kerberos KADM5 + administration system + +SYNOPSIS + kadmin [-r realm] [-p principal] [-q query] [clnt|local args] + clnt args: [-p principal] [[-c ccache]|[-k [-t keytab]]] + [-w] [-s admin_server[:port]] + local args: [-d dbname] [-e \"enc:salt ...\"] [-m] + +DESCRIPTION + kadmin is a command-line interface to the Kerberos KADM5 + administration system. It provides for the maintainance of + Kerberos principals, KADM5 policies, and service key tables + (keytabs). It exists as both a remote client, using Kerberos + authentication and an encrypted RPC to operate securely from + anywhere on the network, and as a local client intended to run + directly on the KDC without Kerberos authentication. The + local version provides all of the functionality of the now + obsolete kdb5_edit(8) except for database dump and load, which + is now provided by the kdb5_util(8) utility. + +COMMAND LINE ARGUMENTS + If -r is specified, then kadmin will use the specified realm + as the default database realm rather than the default realm + for the local machine. + + The -q option allows the passing of a request directly to + kadmin, which will then exit. This can be useful for writing + scripts. + + The remote version authenticates to the KADM5 server using the + service kadmin/admin, and therefore needs a client Kerberos + principal name as which to authenticate. The -p, -c, and -k + are designed to work together to specify which principal as + which to authenticate and where the service ticket or + password/key for that principal should be obtained. If given + the -p option, kadmin will use the specified principal to + authenticate. Otherwise, if given -c option then the primary + principal name of the ccache is used. Otherwise, if given the + -k option, the principal name host/ is used. + Otherwise, kadmin will append "/admin" to the primary + principal name of the default ccache, the value of the USER + environment variable, or the username as obtained with + getpwuid, in order of preference. + + Once kadmin knows the principal name as which to authenticate, + it needs to acquire a Kerberos service ticket for the KADM5 + server. If the -c ccache argument is specified, the ccache + should contain a service ticket for the kadmin/admin service; + it can be acquired with the kinit(1) program. Otherwise, + kadmin requests a new service ticket from the KDC and stores + it in its own temporary ccache. If the -k keytab argument is + specified, the keytab is used to decrypt the KDC response; + otherwise, a password is required. By default, the user is + prompted for the password on the TTY. However, if given the + -w option, kadmin will use the password provided on the + command line instead of prompting for one on the TTY. + WARNING! Placing the password for a Kerberos principal with + administration access into a shell script is EXTREMELY + DANGEROUS and should only be done if you are highly sure that + the script will not fall into the wrong hands. + + If given the -d argument, kadmin will use the specified + database name instead of the default defined in kdc.conf. + Note that specifying a different KDC database name also + specifies a different name for the KADM5 policy database and + lock file. + + If given the -e argument, kadmin will use the specified list + of encryption and salt type tuples instead of the values + specified in kdc.conf. This is useful, for example, if you + want to create a single principal with a particular key/salt + type without affecting any other principals. + + If given the -m argument, kadmin will prompt for the Kerberos + master password on the command line instead of attempting to + use the stash file. + +DATE FORMAT + Various commands in kadmin can take a variety of + date formats, specifying durations or absolute times. + Examples of valid formats are: + + 1 month ago + 2 hours ago + 400000 seconds ago + last year + last Monday + yesterday + a fortnight ago + 3/31/92 10:00:07 PST + January 23, 1987 10:05pm + 22:00 GMT + + Dates which do not have the "ago" specifier default to being + absolute dates, unless they appear in a field where a duration + is expected. In that case the time specifier will be + interpreted as relative. Specifying "ago" on a duration may + result in unexpected behaviour. + +COMMAND DESCRIPTIONS + +add_principal [options] _newprinc_ + creates the principal _newprinc_, prompting twice for a + password. This command requires the "add" privilege. This + command has the aliases "addprinc", "ank". + + OPTIONS + -salt _salttype_ + uses the specified salt instead of the default V5 salt + for generating the key. Valid values for _salttype_ + are: + full_name (aliases "v5_salt", "normal") + name_only + realm_only + no_salt (alias "v4_salt") + + -expire _expdate_ + expiration date of the principal + + -pwexpire _pwexpdate_ + password expiration date + + -maxlife _maxlife_ + maximum ticket life of the principal + + -kvno _kvno_ + explicity set the key version number. This is not + recommended. + + -policy _policy_ + policy used by this principal. If no policy is + supplied, the principal will default to having no + policy, and a warning message will be printed. + + {-|+}allow_tgs_req + "-allow_tgs_req" specifies that a TGS request for a + ticket for a service ticket for this principal is not + permitted. This option is useless for most things. + "+allow_tgs_req" clears this flag. The default is + "+allow_tgs_req". In effect, "-allow_tgs_req" sets + the KRB5_KDB_DISALLOW_TGT_BASED flag on the principal + in the database. + + {-|+}allow_tix + "-allow_tix" forbids the issuance of any tickets for + this principal. "+allow_tix" clears this flag. The + default is "+allow_tix". In effect, "-allow_tix" sets + the KRB5_KDB_DISALLOW_ALL_TIX flag on the principal in + the database. + + {-|+}needchange + "+needchange" sets a flag in attributes field to force + a password change; "-needchange" clears it. The + default is "-needchange". In effect, "+needchange" + sets the KRB5_KDB_REQUIRES_PWCHANGE flag on the + principal in the database. + + {-|+}password_changing_service + "+password_changing_service" sets a flag in the + attributes field marking this as a password change + service principal (useless for most things). + "-password_changing_service" clears the flag. This + flag intentionally has a long name. The default is + "-password_changing_service". In effect, + "+password_changing_service" sets the + KRB5_KDB_PWCHANGE_SERVICE flag on the principal in the + database. + + -randpass + sets the key of the principal to a random value + + -pw _password_ + sets the key of the principal to the specified string + and does not prompt for a password. This is not + recommended. + + EXAMPLE + kadmin: addprinc tlyu/deity + WARNING: no policy specified for "tlyu/deity@ATHENA.MIT.EDU"; + defaulting to no policy. + Enter password for principal tlyu/deity@ATHENA.MIT.EDU: + Re-enter password for principal tlyu/deity@ATHENA.MIT.EDU: + Principal "tlyu/deity@ATHENA.MIT.EDU" created. + kadmin: + + ERRORS + KADM5_AUTH_ADD (requires "add" privilege) + KADM5_BAD_MASK (shouldn't happen) + KADM5_DUP (principal exists already) + KADM5_UNK_POLICY (policy does not exist) + KADM5_PASS_Q_* (password quality violations) + +delete_principal [-force] _principal_ + deletes the specified principal from the database. This + command prompts for deletion, unless the "-force" option is + given. This command requires the "delete" privilege. Aliased + to "delprinc". + + EXAMPLE + kadmin: delprinc mwm_user + Are you sure you want to delete the principal + "mwm_user@ATHENA.MIT.EDU"? (yes/no): yes + Principal "mwm_user@ATHENA.MIT.EDU" deleted. + Make sure that you have removed this principal from + all ACLs before reusing. + kadmin: + + ERRORS + KADM5_AUTH_DELETE (reequires "delete" privilege) + KADM5_UNK_PRINC (principal does not exist) + +modify_principal [options] _principal_ + modifies the specified principal, changing the fields as + specified. The options are as above for "add_principal", + except that password changing is forbidden by this command. + In addition, the option "-clearpolicy" will remove clear the + current policy of a principal. This command requires the + "modify" privilege. Aliased to "modprinc". + + ERRORS + KADM5_AUTH_MODIFY (requires "modify" privilege) + KADM5_UNK_PRINC (principal does not exist) + KADM5_UNK_POLICY (policy does not exist) + KADM5_BAD_MASK (shouldn't happen) + +rename_principal [-force] _old_ _new_ + rename the principal _old_ to _new_. Prompts for + confirmation, unless the "-force" option is given. Requires + both the "add" and "delete" privileges. Aliased to + "renprinc". + + EXAMPLE + kadmin: renprinc tlyutest test0 + Are you sure you want to rename the principal + "tlyutest@ATHENA.MIT.EDU" to + "test0@ATHENA.MIT.EDU"? (yes/no): yes + Principal "tlyutest@ATHENA.MIT.EDU" renamed to + "test0@ATHENA.MIT.EDU". + Make sure that you have removed "tlyutest@ATHENA.MIT.EDU" from + all ACLs before reusing. + kadmin: + + ERRORS + KADM5_AUTH_ADD (requires "add" privilege) + KADM5_AUTH_DELETE (requires "delete" privilege) + KADM5_UNK_PRINC (source principal does not exist) + KADM5_DUP (target principal already exists) + +change_password [options] _principal_ + changes the password of _principal_. Prompts for a new + password if neither -randpass or -pw is specified. Requires + the "modify" privilege, or that the principal that is running + the program to be the same as the one changed. Aliased to + "cpw". + + OPTIONS + -salt _salttype_ + uses the specified salt instead of the default V5 salt + for generating the key. Options are the same as for + add_principal. + + -randpass + sets the key of the principal to a random value + + -pw _password_ + set the password to the specified string. Not + recommended. + + EXAMPLE + kadmin: cpw systest + Enter password for principal systest@ATHENA.MIT.EDU: + Re-enter password for principal systest@ATHENA.MIT.EDU: + Password for systest@ATHENA.MIT.EDU changed. + kadmin: + + ERRORS + KADM5_AUTH_MODIFY (requires the modify privilege) + KADM5_UNK_PRINC (principal does not exist) + KADM5_PASS_Q_* (password policy violation errors) + KADM5_PADD_REUSE (password is in principal's password istory) + KADM5_PASS_TOOSOON (current password minimum life not xpired) + +get_principal [-terse] _principal_ + gets the attributes of _principal_. Requires the "get" + privilege, or that the principal that is running the the + program to be the same as the one being listed. With the + "-terse" option, outputs fields as a quoted tab-separated + strings. Alias "getprinc". + + EXAMPLES + kadmin: getprinc tlyu/deity + Principal: tlyu/deity@ATHENA.MIT.EDU + Key version: 3 + Maximum life: 1 day 00:00:00 + Maximum renewable life: 7 days 00:00:00 + Master key version: 1 + Expires: Mon Jan 18 22:14:07 EDT 2038 + Password expires: Mon Sep 19 14:40:00 EDT 1994 + Password last changed: Mon Jan 31 02:06:40 EDT 1994 + Last modified: by tlyu/admin@ATHENA.MIT.EDU + on Wed Jul 13 18:27:08 EDT 1994 + Attributes: DISALLOW_FORWARDABLE, DISALLOW_PROXIABLE, + REQUIRES_HW_AUTH + Salt type: DEFAULT + kadmin: getprinc systest + systest@ATHENA.MIT.EDU 3 86400 604800 1 + 785926535 753241234 785900000 + tlyu/admin@ATHENA.MIT.EDU 786100034 0 + 0 + kadmin: + + ERRORS + KADM5_AUTH_GET (requires the get privilege) + KADM5_UNK_PRINC (principal does not exist) + +get_principals [expression] + Retrieves all or some principal names. _expression_ is a + shell-style glob expression that can contain the wild-card + characters ?, *, and []'s. All principal names matching the + expression are printed. If no expression is provided, the + expression "*" is assumed. If the expression does not contain + an "@" character, an "@" character followed by the local realm + is appended to the expression. Requires the "list" priviledge. + Alias "getprincs". + + EXAMPLES + kadmin: getprincs test* + test3@SECURE-TEST.OV.COM + test2@SECURE-TEST.OV.COM + test1@SECURE-TEST.OV.COM + testuser@SECURE-TEST.OV.COM + kadmin: + +add_policy [options] _policy_ + adds the named policy to the policy database. Requires the + "add" privilege. Aliased to "addpol". + + OPTIONS + -maxlife _time_ + sets the maximum lifetime of a password + + -minlife _time_ + sets the minimum lifetime of a password + + -minlength _length_ + sets the minimum length of a password + + -minclasses _number_ + sets the minimum number of character classes allowed + in a password + + -history _number_ + sets the number of past keys kept for a principal + + ERRORS + KADM5_AUTH_ADD (requires the add privilege) + KADM5_DUP (policy already exists) + +delete_policy _policy_ + deletes the named policy. Prompts for confirmation before + deletion. The command will fail if the policy is in use by + any principals. Requires the "delete" privilege. Alias + "delpol". + + EXAMPLE + kadmin: del_policy guests + Are you sure you want to delete the policy "guests"? + (yes/no): yes + Policy "guests" deleted. + kadmin: + + ERRORS + KADM5_AUTH_DELETE (requires the delete privilege) + KADM5_UNK_POLICY (policy does not exist) + KADM5_POLICY_REF (reference count on policy is not zero) + +modify_policy [options] _policy_ + modifies the named policy. Options are as above for + "add_policy". Requires the "modify" privilege". Alias + "modpol". + + ERRORS + KADM5_AUTH_MODIFY (requires the modify privilege) + KADM5_UNK_POLICY (policy does not exist) + +get_policy [-terse] _policy_ + displays the values of the named policy. Requires the "get" + privilege. With the "-terse" flag, outputs the fields as + quoted strings separated by tabs. Alias "getpol". + + EXAMPLES + kadmin: get_policy admin + Policy: admin + Maximum password life: 180 days 00:00:00 + Minimum password life: 00:00:00 + Minimum password length: 6 + Minimum number of password character classes: 2 + Number of old keys kept: 5 + Reference count: 17 + kadmin: get_policy -terse admin + admin 15552000 0 6 2 5 17 + kadmin: + + ERRORS + KADM5_AUTH_GET (requires the get privilege) + KADM5_UNK_POLICY (policy does not exist) + +get_policies [expression] + Retrieves all or some policy names. _expression_ is a + shell-style glob expression that can contain the wild-card + characters ?, *, and []'s. All policy names matching the + expression are printed. If no expression is provided, the + expression "*" is assumed. Requires the "list" priviledge. + Alias "getpols". + + EXAMPLES + kadmin: getpols + test-pol + dict-only + once-a-min + test-pol-nopw + kadmin: getpols t* + test-pol + test-pol-nopw + kadmin: + +ktadd [-k keytab] [-q] [principal | -glob princ-exp] [...] + Adds principal or all principals matching princ-exp to a + keytab. princ-exp follows the same rules described for the + get_principals command. An entry for each of the principal's + unique encryption types is added, ignoring multiple keys with + the same encryption type but different salt types. If the -k + argument is not specified, the default keytab /etc/v5srvtab is + used. If the -q option is specified, less verbose status + information is displayed. + + The -glob option requires the "list" privilege. + + EXAMPLES + kadmin% ktadd -k /krb5/kadmind.keytab kadmin/admin kadmin/changepw + kadmin: Entry for principal kadmin/admin@ATHENA.MIT.EDU with + kvno 3, encryption type DES-CBC-CRC added to keytab + WRFILE:/krb5/kadmind.keytab. + kadmin: Entry for principal kadmin/changepw@ATHENA.MIT.EDU + with kvno 3, encryption type DES-CBC-CRC added to keytab + WRFILE:/krb5/kadmind.keytab. + kadmin: + +ktremove [-k keytab] [-q] principal [kvno|"all"|"old"] + Removes entries for the specified principal from a keytab. If + the string "all" is specified, all entries for that principal + are removed; if the string "old" is specified, all entries for + that principal except those with the highest kvno are removed. + Otherwise, the value specified is parsed as an integer, and + all entries whose kvno match that integer are removed. If the + -k argument is not specifeid, the default keytab /etc/v5srvtab + is used. If the -q is specified, less verbose status + information is displayed. + + EXAMPLES + kadmin: ktremove -k /krb5/kadmind.keytab kadmin/admin + kadmin: Entry for principal kadmin/admin with kvno 3 removed + from keytab WRFILE:/krb5/kadmind.keytab. + kadmin: + +SEE ALSO + kerberos(1), kdb5_util(8) + + diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c new file mode 100644 index 000000000..e19383dd9 --- /dev/null +++ b/src/kadmin/cli/kadmin.c @@ -0,0 +1,1322 @@ +/* + * Copyright 1994 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. + * + * kadmin.c: base functions for a kadmin command line interface using + * the OVSecure library + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +/* special struct to convert flag names for principals + to actual krb5_flags for a principal */ +struct pflag { + char *flagname; /* name of flag as typed to CLI */ + int flaglen; /* length of string (not counting -,+) */ + krb5_flags theflag; /* actual principal flag to set/clear */ + int set; /* 0 means clear, 1 means set (on '-') */ +}; + +static struct pflag flags[] = { +{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, +{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, +{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, +{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, +{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, +{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, +{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, +{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, +{"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0}, +{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, +{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, +{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 } +}; + +static char *prflags[] = { + "DISALLOW_POSTDATED", /* 0x00000001 */ + "DISALLOW_FORWARDABLE", /* 0x00000002 */ + "DISALLOW_TGT_BASED", /* 0x00000004 */ + "DISALLOW_RENEWABLE", /* 0x00000008 */ + "DISALLOW_PROXIABLE", /* 0x00000010 */ + "DISALLOW_DUP_SKEY", /* 0x00000020 */ + "DISALLOW_ALL_TIX", /* 0x00000040 */ + "REQUIRES_PRE_AUTH", /* 0x00000080 */ + "REQUIRES_HW_AUTH", /* 0x00000100 */ + "REQUIRES_PWCHANGE", /* 0x00000200 */ + "UNKNOWN_0x00000400", /* 0x00000400 */ + "UNKNOWN_0x00000800", /* 0x00000800 */ + "DISALLOW_SVR", /* 0x00001000 */ + "PWCHANGE_SERVICE" /* 0x00002000 */ +}; + +char *getenv(); +struct passwd *getpwuid(); +int exit_status = 0; +char *def_realm = NULL; +char *whoami = NULL; +time_t get_date(); + +void *handle = NULL; +krb5_context context; +char *ccache_name = NULL; + +void usage() +{ + fprintf(stderr, + "Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args]\n" + "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]\n" + "\tlocal args: [-d dbname] [-e \"enc:salt ...\"] [-m]\n", whoami); + exit(1); +} + +char *strdur(duration) + time_t duration; +{ + static char out[50]; + int days, hours, minutes, seconds; + + days = duration / (24 * 3600); + duration %= 24 * 3600; + hours = duration / 3600; + duration %= 3600; + minutes = duration / 60; + duration %= 60; + seconds = duration; + sprintf(out, "%d %s %02d:%02d:%02d", days, days == 1 ? "day" : "days", + hours, minutes, seconds); + return out; +} + +char *strdate(when) + krb5_timestamp when; +{ + struct tm *tm; + static char out[30]; + + time_t lcltim = when; + tm = localtime(&lcltim); + strftime(out, 30, "%a %b %d %H:%M:%S %Z %Y", tm); + return out; +} + +/* this is a wrapper to go around krb5_parse_principal so we can set + the default realm up properly */ +krb5_error_code kadmin_parse_name(name, principal) + char *name; + krb5_principal *principal; +{ + char *cp, *fullname; + krb5_error_code retval; + + /* assumes def_realm is initialized! */ + fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); + if (fullname == NULL) + return ENOMEM; + strcpy(fullname, name); + cp = strchr(fullname, '@'); + while (cp) { + if (cp - fullname && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '@'); + } + if (cp == NULL) { + strcat(fullname, "@"); + strcat(fullname, def_realm); + } + retval = krb5_parse_name(context, fullname, principal); + free(fullname); + return retval; +} + +char *kadmin_startup(argc, argv) + int argc; + char *argv[]; +{ + extern krb5_kt_ops krb5_ktf_writable_ops; + extern char *optarg; + char *princstr = NULL, *keytab_name = NULL, *query = NULL; + char *password = NULL; + char *luser, *canon, *cp; + int optchar, freeprinc = 0, use_keytab = 0; + struct passwd *pw; + kadm5_ret_t retval; + krb5_ccache cc; + krb5_principal princ; + kadm5_config_params params; + + memset((char *) ¶ms, 0, sizeof(params)); + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "while initializing krb5 library"); + exit(1); + } + krb5_init_ets(context); + + while ((optchar = getopt(argc, argv, "r:p:kq:w:d:s:m:c:t:e:")) != EOF) { + switch (optchar) { + case 'r': + def_realm = optarg; + break; + case 'p': + princstr = optarg; + break; + case 'c': + ccache_name = optarg; + break; + case 'k': + use_keytab++; + break; + case 't': + keytab_name = optarg; + break; + case 'w': + password = optarg; + break; + case 'q': + query = optarg; + break; + case 'd': + params.dbname = optarg; + params.mask |= KADM5_CONFIG_DBNAME; + break; + case 's': + params.admin_server = optarg; + params.mask |= KADM5_CONFIG_ADMIN_SERVER; + break; + case 'm': + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + break; + case 'e': + retval = krb5_string_to_keysalts(optarg, + ", \t", + ":.-", + 0, + ¶ms.keysalts, + ¶ms.num_keysalts); + if (retval) { + com_err(whoami, retval, "while parsing keysalts %s", optarg); + exit(1); + } + params.mask |= KADM5_CONFIG_ENCTYPES; + break; + default: + usage(); + } + } + if ((ccache_name && use_keytab) || + (keytab_name && !use_keytab)) + usage(); + + if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { + if (freeprinc) + free(princstr); + fprintf(stderr, "%s: unable to get default realm\n", whoami); + exit(1); + } + + params.mask |= KADM5_CONFIG_REALM; + params.realm = def_realm; + + /* + * Set cc to an open credentials cache, either specified by the -c + * argument or the default. + */ + if (ccache_name == NULL) { + if (retval = krb5_cc_default(context, &cc)) { + com_err(whoami, retval, + "while opening default credentials cache"); + exit(1); + } + } else { + if (retval = krb5_cc_resolve(context, ccache_name, &cc)) { + com_err(whoami, retval, + "while opening credentials cache %s", ccache_name); + exit(1); + } + } + + /* + * If no principal name is specified: If a ccache was specified + * and its primary principal name can be read, it is used, else if + * a keytab was specified, the principal name is host/hostname, + * otherwise append "/admin" to the primary name of the default + * ccache, $USER, or pw_name. + * + * Gee, 100+ lines to figure out the client principal name. This + * should be compressed... + */ + + if (princstr == NULL) { + if (ccache_name != NULL && + !krb5_cc_get_principal(context, cc, &princ)) { + if (retval = krb5_unparse_name(context, princ, &princstr)) { + com_err(whoami, retval, + "while canonicalizing principal name"); + krb5_free_principal(context, princ); + exit(1); + } + krb5_free_principal(context, princ); + freeprinc++; + } else if (use_keytab != NULL) { + if (retval = krb5_sname_to_principal(context, NULL, + "host", + KRB5_NT_SRV_HST, + &princ)) { + com_err(whoami, retval, + "creating host service principal"); + exit(1); + } + if (retval = krb5_unparse_name(context, princ, &princstr)) { + com_err(whoami, retval, + "while canonicalizing principal name"); + krb5_free_principal(context, princ); + exit(1); + } + krb5_free_principal(context, princ); + freeprinc++; + } else if (!krb5_cc_get_principal(context, cc, &princ)) { + char *realm = NULL; + if (krb5_unparse_name(context, princ, &canon)) { + fprintf(stderr, + "%s: unable to canonicalize principal\n", whoami); + krb5_free_principal(context, princ); + exit(1); + } + /* strip out realm of principal if it's there */ + realm = strchr(canon, '@'); + while (realm) { + if (realm - canon && *(realm - 1) != '\\') + break; + else + realm = strchr(realm, '@'); + } + if (realm) + *realm++ = '\0'; + cp = strchr(canon, '/'); + while (cp) { + if (cp - canon && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '/'); + } + if (cp != NULL) + *cp = '\0'; + princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ + + (realm ? 1 + strlen(realm) : 0) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, canon); + strcat(princstr, "/admin"); + if (realm) { + strcat(princstr, "@"); + strcat(princstr, realm); + } + free(canon); + krb5_free_principal(context, princ); + freeprinc++; + } else if (luser = getenv("USER")) { + princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, luser); + strcat(princstr, "/admin"); + strcat(princstr, "@"); + strcat(princstr, def_realm); + freeprinc++; + } else if (pw = getpwuid(getuid())) { + princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, pw->pw_name); + strcat(princstr, "/admin@"); + strcat(princstr, def_realm); + freeprinc++; + } else { + fprintf(stderr, "%s: unable to figure out a principal name\n", + whoami); + exit(1); + } + } + + /* + * Initialize the kadm5 connection. If we were given a ccache, + * use it. Otherwise, use/prompt for the password. + */ + if (ccache_name) + retval = kadm5_init_with_creds(princstr, cc, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + else if (use_keytab) + retval = kadm5_init_with_skey(princstr, keytab_name, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + else + retval = kadm5_init_with_password(princstr, password, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + if (retval) { + com_err(whoami, retval, "while initializing %s interface", whoami); + if (retval == KADM5_BAD_CLIENT_PARAMS || + retval == KADM5_BAD_SERVER_PARAMS) + usage(); + exit(1); + } + if (freeprinc) + free(princstr); + + if (retval = krb5_cc_close(context, cc)) { + com_err(whoami, retval, "while closing ccache %s", + ccache_name); + exit(1); + } + + /* register the WRFILE keytab type and set it as the default */ + if (retval = krb5_kt_register(context, &krb5_ktf_writable_ops)) { + com_err(whoami, retval, + "while registering writable key table functions"); + exit(1); + } + { +#define DEFAULT_KEYTAB "WRFILE:/etc/v5srvtab" + extern char *krb5_defkeyname; + krb5_defkeyname = DEFAULT_KEYTAB; + } + + return query; +} + +int quit() +{ + krb5_ccache cc; + int retval; + + kadm5_destroy(handle); + if (ccache_name != NULL) { + fprintf(stderr, + "\n\a\a\aAdministration credentials NOT DESTROYED.\n"); + } + + /* insert more random cleanup here */ +} + +void kadmin_delprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_ret_t retval; + krb5_principal princ; + char *canon; + char reply[5]; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: delete_principal [-force] principal\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("delete_principal", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("delete_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Principal \"%s\" not deleted\n", canon); + free(canon); + krb5_free_principal(context, princ); + return; + } + } + retval = kadm5_delete_principal(handle, princ); + krb5_free_principal(context, princ); + if (retval) { + com_err("delete_principal", retval, + "while deleteing principal \"%s\"", canon); + free(canon); + return; + } + printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon); + free(canon); + return; +} + +void kadmin_renprinc(argc, argv) + int argc; + char *argv[]; +{ + krb5_principal oldprinc, newprinc; + char *oldcanon, *newcanon; + char reply[5]; + kadm5_ret_t retval; + + if (! (argc == 3 || + (argc == 4 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: rename_principal [-force] old new\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 2], &oldprinc); + if (retval) { + com_err("rename_principal", retval, "while parsing old principal"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &newprinc); + if (retval) { + krb5_free_principal(context, oldprinc); + com_err("rename_principal", retval, "while parsing new principal"); + return; + } + retval = krb5_unparse_name(context, oldprinc, &oldcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing old principal"); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + retval = krb5_unparse_name(context, newprinc, &newcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing new principal"); + free(oldcanon); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + if (argc == 3) { + printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ", + oldcanon, newcanon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, + "rename_principal: \"%s\" NOT renamed to \"%s\".\n", + oldcanon, newcanon); + free(newcanon); + free(oldcanon); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + } + retval = kadm5_rename_principal(handle, oldprinc, newprinc); + krb5_free_principal(context, oldprinc); + krb5_free_principal(context, newprinc); + if (retval) { + com_err("rename_principal", retval, + "while renaming \"%s\" to \"%s\".", oldcanon, + newcanon); + free(newcanon); + free(oldcanon); + return; + } + printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n", + oldcanon, newcanon, newcanon); + return; +} + +void kadmin_cpw(argc, argv) + int argc; + char *argv[]; +{ + kadm5_ret_t retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + char *canon; + krb5_principal princ; + + if (argc < 2) { + goto usage; + } + + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("change_password", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("change_password", retval, "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) { + retval = kadm5_chpass_principal(handle, princ, argv[2]); + krb5_free_principal(context, princ); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else if ((argc == 3) && (strlen(argv[1]) == 8) && + !strcmp("-randkey", argv[1])) { + retval = kadm5_randkey_principal(handle, princ, NULL, NULL); + krb5_free_principal(context, princ); + if (retval) { + com_err("change_password", retval, + "while randomizing key for \"%s\".", canon); + free(canon); + return; + } + printf("Key for \"%s\" randomized.\n", canon); + free(canon); + return; + } else if (argc == 2) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(context, prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("change_password", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(context, princ); + return; + } + retval = kadm5_chpass_principal(handle, princ, newpw); + krb5_free_principal(context, princ); + memset(newpw, 0, sizeof (newpw)); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else { + free(canon); + krb5_free_principal(context, princ); + usage: + fprintf(stderr, + "usage: change_password [-randpass] [-pw passowrd] " + "principal\n"); + return; + } +} + +int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, caller) + int argc; + char *argv[]; + kadm5_principal_ent_t oprinc; + long *mask; + char **pass; + int *randkey; + char *caller; +{ + int i, j, attrib_set; + time_t date; + time_t now; + krb5_error_code retval; + + *mask = 0; + *pass = NULL; + time(&now); + *randkey = 0; + for (i = 1; i < argc - 1; i++) { + attrib_set = 0; + if (strlen(argv[i]) == 7 && + !strcmp("-expire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + oprinc->princ_expire_time = date == (time_t)-1 ? 0 : date; + *mask |= KADM5_PRINC_EXPIRE_TIME; + continue; + } + } + if (strlen(argv[i]) == 9 && + !strcmp("-pwexpire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + oprinc->pw_expiration = date == (time_t)-1 ? 0 : date; + *mask |= KADM5_PW_EXPIRATION; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-maxlife", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->max_life = get_date(argv[i], NULL) - now; + *mask |= KADM5_MAX_LIFE; + continue; + } + } + if (strlen(argv[i]) == 5 && + !strcmp("-kvno", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->kvno = atoi(argv[i]); + *mask |= KADM5_KVNO; + continue; + } + } + if (strlen(argv[i]) == 7 && + !strcmp("-policy", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->policy = argv[i]; + *mask |= KADM5_POLICY; + continue; + } + } + if (strlen(argv[i]) == 12 && + !strcmp("-clearpolicy", argv[i])) { + oprinc->policy = NULL; + *mask |= KADM5_POLICY_CLR; + continue; + } + if (strlen(argv[i]) == 3 && + !strcmp("-pw", argv[i])) { + if (++i > argc - 2) + return -1; + else { + *pass = argv[i]; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-randkey", argv[i])) { + ++*randkey; + continue; + } + for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { + if (strlen(argv[i]) == flags[j].flaglen + 1 && + !strcmp(flags[j].flagname, + &argv[i][1] /* strip off leading + or - */)) { + if (flags[j].set && argv[i][0] == '-' || + !flags[j].set && argv[i][0] == '+') { + oprinc->attributes |= flags[j].theflag; + *mask |= KADM5_ATTRIBUTES; + attrib_set++; + break; + } else if (flags[j].set && argv[i][0] == '+' || + !flags[j].set && argv[i][0] == '-') { + oprinc->attributes &= ~flags[j].theflag; + *mask |= KADM5_ATTRIBUTES; + attrib_set++; + break; + } else { + return -1; + } + } + } + if (!attrib_set) + return -1; /* nothing was parsed */ + } + if (i != argc - 1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } + retval = kadmin_parse_name(argv[i], &oprinc->principal); + if (retval) { + com_err(caller, retval, "while parsing principal"); + return -1; + } + return 0; +} + +void kadmin_addmodprinc_usage(func) + char *func; +{ + fprintf(stderr, "usage: %s [options] principal\n", func); + fprintf(stderr, "\toptions are:\n"); + fprintf(stderr, "\t\t[-salt salttype] [-expire expdate] [-pwexpire pwexpdate]\n\t\t[-maxlife maxtixlife] [-kvno kvno] [-policy policy]\n\t\t[-randkey] [-pw password] [{+|-}attribute]\n"); + fprintf(stderr, "\tattributes are:\n"); + fprintf(stderr, "\t\tallow_tgs_req, allow_tix, needchange, password_changing_service\n"); +} + +void kadmin_addprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec princ; + long mask; + int randkey = 0; + char *pass, *canon; + krb5_error_code retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, &randkey, + "add_principal")) { + kadmin_addmodprinc_usage("add_principal"); + return; + } + retval = krb5_unparse_name(context, princ.principal, &canon); + if (retval) { + com_err("add_principal", + retval, "while canonicalizing principal"); + krb5_free_principal(context, princ.principal); + return; + } + if (randkey) { /* do special stuff if -randkey specified */ + princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; /* set notix */ + mask |= KADM5_ATTRIBUTES; + pass = "dummy"; + } else if (pass == NULL) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + canon); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + canon); + retval = krb5_read_password(context, prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("add_principal", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(context, princ.principal); + return; + } + pass = newpw; + } + mask |= KADM5_PRINCIPAL; + retval = kadm5_create_principal(handle, &princ, mask, pass); + if (retval) { + com_err("add_principal", retval, "while creating \"%s\".", + canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + if (randkey) { /* more special stuff for -randkey */ + retval = kadm5_randkey_principal(handle, princ.principal, + NULL, NULL); + if (retval) { + com_err("add_principal", retval, + "while randomizing key for \"%s\".", canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */ + mask = KADM5_ATTRIBUTES; + retval = kadm5_modify_principal(handle, &princ, mask); + if (retval) { + com_err("add_principal", retval, + "while clearing DISALLOW_ALL_TIX for \"%s\".", canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + } + krb5_free_principal(context, princ.principal); + printf("Principal \"%s\" created.\n", canon); + free(canon); +} + +void kadmin_modprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec princ, oldprinc; + krb5_principal kprinc; + long mask; + krb5_error_code retval; + char *pass, *canon; + int randkey = 0; + + if (argc < 2) { + kadmin_addmodprinc_usage("modify_principal"); + return; + } + + retval = kadmin_parse_name(argv[argc - 1], &kprinc); + if (retval) { + com_err("modify_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(context, kprinc, &canon); + if (retval) { + com_err("modify_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(context, kprinc); + return; + } + retval = kadm5_get_principal(handle, kprinc, &oldprinc, + KADM5_PRINCIPAL_NORMAL_MASK); + krb5_free_principal(context, kprinc); + if (retval) { + com_err("modify_principal", retval, "while getting \"%s\".", + canon); + free(canon); + return; + } + princ.attributes = oldprinc.attributes; + kadm5_free_principal_ent(handle, &oldprinc); + retval = kadmin_parse_princ_args(argc, argv, + &princ, &mask, + &pass, &randkey, + "modify_principal"); + if (retval) { + kadmin_addmodprinc_usage("modify_principal"); + free(canon); + return; + } + if (randkey) { + fprintf(stderr, "modify_principal: -randkey not allowed\n"); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + retval = kadm5_modify_principal(handle, &princ, mask); + krb5_free_principal(context, princ.principal); + if (retval) { + com_err("modify_principal", retval, + "while modifying \"%s\".", canon); + free(canon); + return; + } + printf("Principal \"%s\" modified.\n", canon); + free(canon); +} + +void kadmin_getprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec dprinc; + krb5_principal princ; + krb5_error_code retval; + char *canon, *modcanon; + int i; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-terse", argv[1])))) { + fprintf(stderr, "usage: get_principal [-terse] principal\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("get_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("get_principal", retval, "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + retval = kadm5_get_principal(handle, princ, &dprinc, + KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA); + krb5_free_principal(context, princ); + if (retval) { + com_err("get_principal", retval, "while retrieving \"%s\".", canon); + free(canon); + return; + } + retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon); + if (retval) { + com_err("get_principal", retval, "while unparsing modname"); + kadm5_free_principal_ent(handle, &dprinc); + free(canon); + return; + } + if (argc == 2) { + printf("Principal: %s\n", canon); + printf("Expiration date: %s\n", strdate(dprinc.princ_expire_time)); + printf("Last password change: %s\n", + strdate(dprinc.last_pwd_change)); + printf("Password expiration date: %s\n", + dprinc.pw_expiration ? + strdate(dprinc.pw_expiration) : "[none]"); + printf("Maximum ticket life: %s\n", strdur(dprinc.max_life)); + printf("Last modified: by %s\n\ton %s\n", + modcanon, strdate(dprinc.mod_date)); + printf("Last successful authentication: %s\n", + strdate(dprinc.last_success)); + printf("Last failed authentication: %s\n", + strdate(dprinc.last_failed)); + printf("Failed password attempts: %d\n", + dprinc.fail_auth_count); + printf("Number of keys: %d\n", dprinc.n_key_data); + for (i = 0; i < dprinc.n_key_data; i++) { + krb5_key_data *key_data = &dprinc.key_data[i]; + char enctype[BUFSIZ], salttype[BUFSIZ]; + + if (krb5_enctype_to_string(key_data->key_data_type[0], + enctype, sizeof(enctype))) + sprintf(enctype, "", + key_data->key_data_type[0]); + printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype); + if (key_data->key_data_ver > 1) { + if (krb5_salttype_to_string(key_data->key_data_type[1], + salttype, sizeof(salttype))) + sprintf(salttype, "", + key_data->key_data_type[1]); + printf("%s\n", salttype); + } else + printf("no salt\n"); + } + + printf("Attributes:"); + for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { + if (dprinc.attributes & (krb5_flags) 1 << i) + printf(" %s", prflags[i]); + } + printf("\n"); + printf("Policy: %s\n", dprinc.policy ? dprinc.policy : "[none]"); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"" + "\t%d\t%d\t%d\t%d\t%d", + canon, dprinc.princ_expire_time, dprinc.last_pwd_change, + dprinc.pw_expiration, dprinc.max_life, modcanon, + dprinc.mod_date, dprinc.attributes, dprinc.kvno, + dprinc.mkvno, dprinc.policy, + dprinc.max_renewable_life, dprinc.last_success, + dprinc.last_failed, dprinc.fail_auth_count, + dprinc.n_key_data); + for (i = 0; i < dprinc.n_key_data; i++) + printf("\t%d\t%d\t%d\t%d", + dprinc.key_data[i].key_data_ver, + dprinc.key_data[i].key_data_kvno, + dprinc.key_data[i].key_data_type[0], + dprinc.key_data[i].key_data_type[1]); + printf("\n"); + } + free(modcanon); + kadm5_free_principal_ent(handle, &dprinc); + free(canon); +} + +void kadmin_getprincs(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char *exp, **names; + int i, count; + + exp = NULL; + if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { + fprintf(stderr, "usage: get_principals [expression]\n"); + return; + } + retval = kadm5_get_principals(handle, exp, &names, &count); + if (retval) { + com_err("get_principals", retval, "while retrieving list."); + return; + } + for (i = 0; i < count; i++) + printf("%s\n", names[i]); + kadm5_free_name_list(handle, names, count); +} + +int kadmin_parse_policy_args(argc, argv, policy, mask, caller) + int argc; + char *argv[]; + kadm5_policy_ent_t policy; + long *mask; + char *caller; +{ + int i; + time_t now; + time_t date; + krb5_error_code retval; + + time(&now); + *mask = 0; + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-maxlife")) { + if (++i > argc -2) + return -1; + else { + date = get_date(argv[i], NULL); + policy->pw_max_life = + (date == (time_t)-1 ? 0 : date) - now; + *mask |= KADM5_PW_MAX_LIFE; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-minlife")) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + policy->pw_min_life = + (date == (time_t)-1 ? 0 : date) - now; + *mask |= KADM5_PW_MIN_LIFE; + continue; + } + } else if (strlen(argv[i]) == 10 && + !strcmp(argv[i], "-minlength")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_length = atoi(argv[i]); + *mask |= KADM5_PW_MIN_LENGTH; + continue; + } + } else if (strlen(argv[i]) == 11 && + !strcmp(argv[i], "-minclasses")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_classes = atoi(argv[i]); + *mask |= KADM5_PW_MIN_CLASSES; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-history")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_history_num = atoi(argv[i]); + *mask |= KADM5_PW_HISTORY_NUM; + continue; + } + } else + return -1; + } + if (i != argc -1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } else + return 0; +} + +void kadmin_addmodpol_usage(func) + char *func; +{ + fprintf(stderr, "usage; %s [options] policy\n", func); + fprintf(stderr, "\toptions are:\n"); + fprintf(stderr, "\t\t[-maxlife time] [-minlife time] [-minlength length]\n\t\t[-minclasses number] [-history number]\n"); +} + +void kadmin_addpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + long mask; + kadm5_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { + kadmin_addmodpol_usage("add_policy"); + return; + } else { + policy.policy = argv[argc - 1]; + mask |= KADM5_POLICY; + retval = kadm5_create_policy(handle, &policy, mask); + if (retval) { + com_err("add_policy", retval, "while creating policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_modpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + long mask; + kadm5_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, + "modify_policy")) { + kadmin_addmodpol_usage("modify_policy"); + return; + } else { + policy.policy = argv[argc - 1]; + retval = kadm5_modify_policy(handle, &policy, mask); + if (retval) { + com_err("modify_policy", retval, "while modifying policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_delpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char reply[5]; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: delete_policy [-force] policy\n"); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]); + return; + } + } + retval = kadm5_delete_policy(handle, argv[argc - 1]); + if (retval) { + com_err("delete_policy:", retval, "while deleting policy \"%s\"", + argv[argc - 1]); + return; + } + return; +} + +void kadmin_getpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + kadm5_policy_ent_rec policy; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-terse", argv[1])))) { + fprintf(stderr, "usage: get_policy [-terse] policy\n"); + return; + } + retval = kadm5_get_policy(handle, argv[argc - 1], &policy); + if (retval) { + com_err("get_policy", retval, "while retrieving policy \"%s\".", + argv[argc - 1]); + return; + } + if (argc == 2) { + printf("Policy: %s\n", policy.policy); + printf("Maximum password life: %d\n", policy.pw_max_life); + printf("Minimum password life: %d\n", policy.pw_min_life); + printf("Minimum password length: %d\n", policy.pw_min_length); + printf("Minimum number of password character classes: %d\n", + policy.pw_min_classes); + printf("Number of old keys kept: %d\n", policy.pw_history_num); + printf("Reference count: %d\n", policy.policy_refcnt); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n", + policy.policy, policy.pw_max_life, policy.pw_min_life, + policy.pw_min_length, policy.pw_min_classes, + policy.pw_history_num, policy.policy_refcnt); + } + kadm5_free_policy_ent(handle, &policy); + return; +} + +void kadmin_getpols(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char *exp, **names; + int i, count; + + exp = NULL; + if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { + fprintf(stderr, "usage: get_policies [expression]\n"); + return; + } + retval = kadm5_get_policies(handle, exp, &names, &count); + if (retval) { + com_err("get_policies", retval, "while retrieving list."); + return; + } + for (i = 0; i < count; i++) + printf("%s\n", names[i]); + kadm5_free_name_list(handle, names, count); +} + +kadmin_getprivs(argc, argv) + int argc; + char *argv[]; +{ + static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"}; + krb5_error_code retval; + int i; + long plist; + + if (argc != 1) { + fprintf(stderr, "usage: get_privs\n"); + return; + } + retval = kadm5_get_privs(handle, &plist); + if (retval) { + com_err("get_privs", retval, "while retrieving privileges"); + return; + } + printf("current privileges:"); + for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { + if (plist & 1 << i) + printf(" %s", privs[i]); + } + printf("\n"); + return; +} diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct new file mode 100644 index 000000000..d22229bf0 --- /dev/null +++ b/src/kadmin/cli/kadmin_ct.ct @@ -0,0 +1,79 @@ +# Copyright 1994 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. +# +# +# Command table for kadmin CLI for OVSecure +# + +command_table kadmin_cmds; + +request kadmin_addprinc, "Add principal", + add_prinicpal, addprinc, ank; + +request kadmin_delprinc, "Delete principal", + delete_principal, delprinc; + +request kadmin_modprinc, "Modify principal", + modify_principal, modprinc; + +request kadmin_renprinc, "Rename principal", + rename_principal, renprinc; + +request kadmin_cpw, "Change password", + change_password, cpw; + +request kadmin_getprinc, "Get principal", + get_principal, getprinc; + +request kadmin_getprincs, "Get principals", + get_principals, getprincs; + +request kadmin_addpol, "Add policy", + add_policy, addpol; + +request kadmin_modpol, "Modify policy", + modify_policy, modpol; + +request kadmin_delpol, "Delete policy", + delete_policy, delpol; + +request kadmin_getpol, "Get policy", + get_policy, getpol; + +request kadmin_getpols, "Get policies", + get_policies, getpols; + +request kadmin_getprivs, "Get privileges", + get_privs, getprivs; + +request kadmin_keytab_add, "Add entry(s) to a keytab", + ktadd, xst; + +request kadmin_keytab_remove, "Remove entry(s) from a keytab", + ktremove, ktrem; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; + diff --git a/src/kadmin/cli/keytab.c b/src/kadmin/cli/keytab.c new file mode 100644 index 000000000..4b51140b5 --- /dev/null +++ b/src/kadmin/cli/keytab.c @@ -0,0 +1,420 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include + +#include +#include +#include + +static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, + char *princ_str); +static int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str); +static char *etype_string(krb5_enctype enctype); + +extern char *krb5_defkeyname; +extern char *whoami; +extern krb5_context context; +extern void *handle; +static int quiet; + +void add_usage() +{ + fprintf(stderr, "Usage: ktadd [-k[eytab] keytab] [-q] [principal | -glob princ-exp] [...]\n"); +} + +void rem_usage() +{ + fprintf(stderr, "Usage: ktremove [-k[eytab] keytab] [-q] principal [kvno|\"all\"|\"old\"]\n"); +} + +int process_keytab(krb5_context context, char **keytab_str, + krb5_keytab *keytab) +{ + int code; + + if (*keytab_str == NULL) { + if (! (*keytab_str = strdup(krb5_defkeyname))) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + code = krb5_kt_default(context, keytab); + if (code != 0) { + com_err(whoami, code, "while opening default keytab"); + free(*keytab_str); + return 1; + } + } else { + if (strchr(*keytab_str, ':') != NULL) { + *keytab_str = strdup(*keytab_str); + if (*keytab_str == NULL) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + } else { + char *tmp = *keytab_str; + + *keytab_str = (char *) + malloc(strlen("WRFILE:")+strlen(tmp)+1); + if (*keytab_str == NULL) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + sprintf(*keytab_str, "WRFILE:%s", tmp); + } + + code = krb5_kt_resolve(context, *keytab_str, keytab); + if (code != 0) { + com_err(whoami, code, "while resolving keytab %s", *keytab_str); + free(keytab_str); + return 1; + } + } + + return 0; +} + + +void kadmin_keytab_add(int argc, char **argv) +{ + krb5_keytab keytab = 0; + char *princ_str, *keytab_str = NULL, **princs; + int code, num, i; + + argc--; argv++; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc || keytab_str) { + add_usage(); + return; + } + keytab_str = *argv; + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } else + break; + argc--; argv++; + } + + if (argc == 0) { + add_usage(); + return; + } + + if (process_keytab(context, &keytab_str, &keytab)) + return; + + while (*argv) { + if (strcmp(*argv, "-glob") == 0) { + if (*++argv == NULL) { + add_usage(); + break; + } + + if (code = kadm5_get_principals(handle, *argv, &princs, &num)) { + com_err(whoami, code, "while expanding expression \"%s\".", + *argv); + argv++; + continue; + } + + for (i = 0; i < num; i++) + (void) add_principal(handle, keytab_str, keytab, + princs[i]); + kadm5_free_name_list(handle, princs, num); + } else + (void) add_principal(handle, keytab_str, keytab, *argv); + argv++; + } + + code = krb5_kt_close(context, keytab); + if (code != 0) + com_err(whoami, code, "while closing keytab"); + + free(keytab_str); +} + +void kadmin_keytab_remove(int argc, char **argv) +{ + krb5_keytab keytab = 0; + char *princ_str, *keytab_str = NULL; + int code; + + argc--; argv++; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc || keytab_str) { + rem_usage(); + return; + } + keytab_str = *argv; + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } else + break; + argc--; argv++; + } + + if (argc != 1 && argc != 2) { + rem_usage(); + return; + } + if (process_keytab(context, &keytab_str, &keytab)) + return; + + (void) remove_principal(keytab_str, keytab, argv[0], argv[1]); + + code = krb5_kt_close(context, keytab); + if (code != 0) + com_err(whoami, code, "while closing keytab"); + + free(keytab_str); +} + +int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, + char *princ_str) +{ + kadm5_principal_ent_rec princ_rec; + krb5_principal princ; + krb5_keytab_entry new_entry; + krb5_keyblock *keys; + int code, code2, mask, nkeys, i; + + (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); + + princ = NULL; + keys = NULL; + nkeys = 0; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing -add principal name %s", + princ_str); + goto cleanup; + } + + code = kadm5_randkey_principal(handle, princ, &keys, &nkeys); + if (code != 0) { + if (code == KADM5_UNK_PRINC) { + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, princ_str); + } else + com_err(whoami, code, "while changing %s's key", + princ_str); + goto cleanup; + } + + code = kadm5_get_principal(handle, princ, &princ_rec, + KADM5_PRINCIPAL_NORMAL_MASK); + if (code != 0) { + com_err(whoami, code, "while retrieving principal"); + goto cleanup; + } + + for (i = 0; i < nkeys; i++) { + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = princ; + new_entry.key = keys[i]; + new_entry.vno = princ_rec.kvno; + + code = krb5_kt_add_entry(context, keytab, &new_entry); + if (code != 0) { + com_err(whoami, code, "while adding key to keytab"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + + if (!quiet) + printf("%s: Entry for principal %s with kvno %d, " + "encryption type %s added to keytab %s.\n", + whoami, princ_str, princ_rec.kvno, + etype_string(keys[i].enctype), keytab_str); + } + + code = kadm5_free_principal_ent(handle, &princ_rec); + if (code != 0) { + com_err(whoami, code, "while freeing principal entry"); + goto cleanup; + } + +cleanup: + if (nkeys) { + for (i = 0; i < nkeys; i++) + krb5_free_keyblock(context, &keys[i]); + free(keys); + } + if (princ) + krb5_free_principal(context, princ); + + return code; +} + +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str) +{ + krb5_principal princ; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; + int code, kvno, did_something; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing principal name %s", + princ_str); + return code; + } + + mode = UNDEF; + if (kvno_str == NULL) { + mode = HIGH; + kvno = 0; + } else if (strcmp(kvno_str, "all") == 0) { + mode = ALL; + kvno = 0; + } else if (strcmp(kvno_str, "old") == 0) { + mode = OLD; + kvno = 0; + } else { + mode = SPEC; + kvno = atoi(kvno_str); + } + + /* kvno is set to specified value for SPEC, 0 otherwise */ + code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); + if (code != 0) { + if (code == ENOENT) { + fprintf(stderr, "%s: Keytab %s does not exist.\n", + whoami, keytab_str); + } else if (code == KRB5_KT_NOTFOUND) { + if (mode != SPEC) + fprintf(stderr, "%s: No entry for principal " + "%s exists in keytab %s\n", + whoami, princ_str, keytab_str); + else + fprintf(stderr, "%s: No entry for principal " + "%s with kvno %d exists in keytab " + "%s.\n", whoami, princ_str, kvno, + keytab_str); + } else { + com_err(whoami, code, "while retrieving highest kvno " + "from keytab"); + } + return code; + } + + /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ + kvno = entry.vno; + krb5_kt_free_entry(context, &entry); + + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while starting keytab scan"); + return code; + } + + did_something = 0; + while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { + if (krb5_principal_compare(context, princ, entry.principal) && + ((mode == ALL) || + (mode == SPEC && entry.vno == kvno) || + (mode == OLD && entry.vno != kvno) || + (mode == HIGH && entry.vno == kvno))) { + + /* + * Ack! What a kludge... the scanning functions lock + * the keytab so entries cannot be removed while they + * are operating. + */ + code = krb5_kt_end_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while temporarily ending " + "keytab scan"); + return code; + } + code = krb5_kt_remove_entry(context, keytab, &entry); + if (code != 0) { + com_err(whoami, code, "while deleting entry from keytab"); + return code; + } + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while restarting keytab scan"); + return code; + } + + did_something++; + if (!quiet) + printf("%s: Entry for principal %s with kvno %d " + "removed from keytab %s.\n", whoami, + princ_str, entry.vno, keytab_str); + } + krb5_kt_free_entry(context, &entry); + } + if (code && code != KRB5_KT_END) { + com_err(whoami, code, "while scanning keytab"); + return code; + } + if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) { + com_err(whoami, code, "while ending keytab scan"); + return code; + } + + /* + * If !did_someting then mode must be OLD or we would have + * already returned with an error. But check it anyway just to + * prevent unexpected error messages... + */ + if (!did_something && mode == OLD) { + fprintf(stderr, "%s: There is only one entry for principal " + "%s in keytab %s\n", whoami, princ_str, keytab_str); + return 1; + } + + return 0; +} + +/* + * etype_string(enctype): return a string representation of the + * encryption type. XXX copied from klist.c; this should be a + * library function, or perhaps just #defines + */ +static char *etype_string(enctype) + krb5_enctype enctype; +{ + static char buf[12]; + + switch (enctype) { + case ENCTYPE_DES_CBC_CRC: + return "DES-CBC-CRC"; + break; + case ENCTYPE_DES_CBC_MD4: + return "DES-CBC-MD4"; + break; + case ENCTYPE_DES_CBC_MD5: + return "DES-CBC-MD5"; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + return "DES3-CBC-MD5"; + break; +#endif + default: + sprintf(buf, "etype %d", enctype); + return buf; + break; + } +} diff --git a/src/kadmin/cli/memmove.c b/src/kadmin/cli/memmove.c new file mode 100644 index 000000000..abc91e923 --- /dev/null +++ b/src/kadmin/cli/memmove.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#define MEMMOVE + +/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */ + +#include +#include +#ifdef USE_STRING_H +#include +#else +#include +#endif + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/src/kadmin/cli/setenv.c b/src/kadmin/cli/setenv.c new file mode 100644 index 000000000..a2432c3d6 --- /dev/null +++ b/src/kadmin/cli/setenv.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */ +/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */ + +#ifndef __STDC__ +#define const +#endif + +#include +#include +#include + +#ifndef __P +#define __P(x) () +#endif +char *__findenv __P((const char *, int *)); + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *c; + int l_value, offset; + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((c = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(c) >= l_value) { /* old larger; copy over */ + while (*c++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **p; + + for (p = environ, cnt = 0; *p; ++p, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2))); + if (!p) + return (-1); + memcpy(p, environ, cnt * sizeof(char *)); + environ = p; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(c - name) + l_value + 2)))) + return (-1); + for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); + for (*c++ = '='; *c++ = *value++;); + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **p; + int offset; + + while (__findenv(name, &offset)) /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; +} + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + + return (__findenv(name, &offset)); +} + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + */ +static char * +__findenv(name, offset) + register const char *name; + int *offset; +{ + extern char **environ; + register int len; + register const char *np; + register char **p, *c; + + if (name == NULL || environ == NULL) + return (NULL); + for (np = name; *np && *np != '='; ++np) + continue; + len = np - name; + for (p = environ; (c = *p) != NULL; ++p) + if (strncmp(c, name, len) == 0 && c[len] == '=') { + *offset = p - environ; + return (c + len + 1); + } + return (NULL); +} diff --git a/src/kadmin/cli/ss_wrapper.c b/src/kadmin/cli/ss_wrapper.c new file mode 100644 index 000000000..89e94b36b --- /dev/null +++ b/src/kadmin/cli/ss_wrapper.c @@ -0,0 +1,61 @@ +/* + * Copyright 1994 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. + * + * + * ss wrapper for kadmin + */ + +#include +#include +#include +#include + +extern ss_request_table kadmin_cmds; +extern int exit_status; +extern char *kadmin_startup(); +extern char *whoami; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + whoami = ((whoami = strrchr(argv[0], '/')) ? whoami+1 : argv[0]); + + request = kadmin_startup(argc, argv); + sci_idx = ss_create_invocation(whoami, "5.0", (char *) NULL, + &kadmin_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + if (request) { + code = ss_execute_line(sci_idx, request); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/cli/strftime.c b/src/kadmin/cli/strftime.c new file mode 100644 index 000000000..484852a72 --- /dev/null +++ b/src/kadmin/cli/strftime.c @@ -0,0 +1,469 @@ +/* strftime - custom formatting of date and/or time + Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Note: this version of strftime lacks locale support, + but it is standalone. + + Performs `%' substitutions similar to those in printf. Except + where noted, substituted fields have a fixed size; numeric fields are + padded if necessary. Padding is with zeros by default; for fields + that display a single number, padding can be changed or inhibited by + following the `%' with one of the modifiers described below. Unknown + field specifiers are copied as normal characters. All other + characters are copied to the output without change. + + Supports a superset of the ANSI C field specifiers. + + Literal character fields: + % % + n newline + t tab + + Numeric modifiers (a nonstandard extension): + - do not pad the field + _ pad the field with spaces + + Time fields: + %H hour (00..23) + %I hour (01..12) + %k hour ( 0..23) + %l hour ( 1..12) + %M minute (00..59) + %p locale's AM or PM + %r time, 12-hour (hh:mm:ss [AP]M) + %R time, 24-hour (hh:mm) + %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension) + %S second (00..61) + %T time, 24-hour (hh:mm:ss) + %X locale's time representation (%H:%M:%S) + %Z time zone (EDT), or nothing if no time zone is determinable + + Date fields: + %a locale's abbreviated weekday name (Sun..Sat) + %A locale's full weekday name, variable length (Sunday..Saturday) + %b locale's abbreviated month name (Jan..Dec) + %B locale's full month name, variable length (January..December) + %c locale's date and time (Sat Nov 04 12:02:33 EST 1989) + %C century (00..99) + %d day of month (01..31) + %e day of month ( 1..31) + %D date (mm/dd/yy) + %h same as %b + %j day of year (001..366) + %m month (01..12) + %U week number of year with Sunday as first day of week (00..53) + %w day of week (0..6) + %W week number of year with Monday as first day of week (00..53) + %x locale's date representation (mm/dd/yy) + %y last two digits of year (00..99) + %Y year (1970...) + + David MacKenzie */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)) +#include +#else +#include +#endif + +#ifndef STDC_HEADERS +time_t mktime (); +#endif + +#if defined(HAVE_TZNAME) +extern char *tzname[2]; +#endif + +/* Types of padding for numbers in date and time. */ +enum padding +{ + none, blank, zero +}; + +static char const* const days[] = +{ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static char const * const months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +/* Add character C to STRING and increment LENGTH, + unless LENGTH would exceed MAX. */ + +#define add_char(c) \ + do \ + { \ + if (length + 1 <= max) \ + string[length++] = (c); \ + } \ + while (0) + +/* Add a 2 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num2 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Add a 3 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num3 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 100; + int mid = (num - top * 100) / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + if (mid == 0 && top == 0 && pad == blank) + add_char (' '); + else if (mid != 0 || top != 0 || pad == zero) + add_char (mid + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Like strncpy except return the number of characters copied. */ + +static int +add_str (to, from, max) + char *to; + const char *from; + int max; +{ + int i; + + for (i = 0; from[i] && i <= max; ++i) + to[i] = from[i]; + return i; +} + +static int +add_num_time_t (string, max, num) + char *string; + int max; + time_t num; +{ + /* This buffer is large enough to hold the character representation + (including the trailing NUL) of any unsigned decimal quantity + whose binary representation fits in 128 bits. */ + char buf[40]; + int length; + + if (sizeof (num) > 16) + abort (); + sprintf (buf, "%lu", (unsigned long) num); + length = add_str (string, buf, max); + return length; +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Sundays. */ + +static int +sun_week (tm) + struct tm *tm; +{ + int dl; + + /* Set `dl' to the day in the year of the last day of the week previous + to the one containing the day specified in TM. If the day specified + in TM is in the first week of the year, `dl' will be negative or 0. + Otherwise, calculate the number of complete weeks before our week + (dl / 7) and add any partial week at the start of the year (dl % 7). */ + dl = tm->tm_yday - tm->tm_wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Mondays. */ + +static int +mon_week (tm) + struct tm *tm; +{ + int dl, wday; + + if (tm->tm_wday == 0) + wday = 6; + else + wday = tm->tm_wday - 1; + dl = tm->tm_yday - wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME) +char * +zone_name (tp) + struct tm *tp; +{ + char *timezone (); + struct timeval tv; + struct timezone tz; + + gettimeofday (&tv, &tz); + return timezone (tz.tz_minuteswest, tp->tm_isdst); +} +#endif + +/* Format the time given in TM according to FORMAT, and put the + results in STRING. + Return the number of characters (not including terminating null) + that were put into STRING, or 0 if the length would have + exceeded MAX. */ + +size_t +strftime (string, max, format, tm) + char *string; + size_t max; + const char *format; + const struct tm *tm; +{ + enum padding pad; /* Type of padding to apply. */ + size_t length = 0; /* Characters put in STRING so far. */ + + for (; *format && length < max; ++format) + { + if (*format != '%') + add_char (*format); + else + { + ++format; + /* Modifiers: */ + if (*format == '-') + { + pad = none; + ++format; + } + else if (*format == '_') + { + pad = blank; + ++format; + } + else + pad = zero; + + switch (*format) + { + /* Literal character fields: */ + case 0: + case '%': + add_char ('%'); + break; + case 'n': + add_char ('\n'); + break; + case 't': + add_char ('\t'); + break; + default: + add_char (*format); + break; + + /* Time fields: */ + case 'H': + case 'k': + length += + add_num2 (&string[length], tm->tm_hour, max - length, + *format == 'H' ? pad : blank); + break; + case 'I': + case 'l': + { + int hour12; + + if (tm->tm_hour == 0) + hour12 = 12; + else if (tm->tm_hour > 12) + hour12 = tm->tm_hour - 12; + else + hour12 = tm->tm_hour; + length += + add_num2 (&string[length], hour12, max - length, + *format == 'I' ? pad : blank); + } + break; + case 'M': + length += + add_num2 (&string[length], tm->tm_min, max - length, pad); + break; + case 'p': + if (tm->tm_hour < 12) + add_char ('A'); + else + add_char ('P'); + add_char ('M'); + break; + case 'r': + length += + strftime (&string[length], max - length, "%I:%M:%S %p", tm); + break; + case 'R': + length += + strftime (&string[length], max - length, "%H:%M", tm); + break; + + case 's': + { + struct tm writable_tm; + writable_tm = *tm; + length += add_num_time_t (&string[length], max - length, + mktime (&writable_tm)); + } + break; + + case 'S': + length += + add_num2 (&string[length], tm->tm_sec, max - length, pad); + break; + case 'T': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'X': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'Z': +#ifdef HAVE_TM_ZONE + length += add_str (&string[length], tm->tm_zone, max - length); +#else +#ifdef HAVE_TZNAME + if (tm->tm_isdst && tzname[1] && *tzname[1]) + length += add_str (&string[length], tzname[1], max - length); + else + length += add_str (&string[length], tzname[0], max - length); +#else + length += add_str (&string[length], zone_name (tm), max - length); +#endif +#endif + break; + + /* Date fields: */ + case 'a': + add_char (days[tm->tm_wday][0]); + add_char (days[tm->tm_wday][1]); + add_char (days[tm->tm_wday][2]); + break; + case 'A': + length += + add_str (&string[length], days[tm->tm_wday], max - length); + break; + case 'b': + case 'h': + add_char (months[tm->tm_mon][0]); + add_char (months[tm->tm_mon][1]); + add_char (months[tm->tm_mon][2]); + break; + case 'B': + length += + add_str (&string[length], months[tm->tm_mon], max - length); + break; + case 'c': + length += + strftime (&string[length], max - length, + "%a %b %d %H:%M:%S %Z %Y", tm); + break; + case 'C': + length += + add_num2 (&string[length], (tm->tm_year + 1900) / 100, + max - length, pad); + break; + case 'd': + length += + add_num2 (&string[length], tm->tm_mday, max - length, pad); + break; + case 'e': + length += + add_num2 (&string[length], tm->tm_mday, max - length, blank); + break; + case 'D': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'j': + length += + add_num3 (&string[length], tm->tm_yday + 1, max - length, pad); + break; + case 'm': + length += + add_num2 (&string[length], tm->tm_mon + 1, max - length, pad); + break; + case 'U': + length += + add_num2 (&string[length], sun_week (tm), max - length, pad); + break; + case 'w': + add_char (tm->tm_wday + '0'); + break; + case 'W': + length += + add_num2 (&string[length], mon_week (tm), max - length, pad); + break; + case 'x': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'y': + length += + add_num2 (&string[length], tm->tm_year % 100, + max - length, pad); + break; + case 'Y': + add_char ((tm->tm_year + 1900) / 1000 + '0'); + length += + add_num3 (&string[length], + (1900 + tm->tm_year) % 1000, max - length, zero); + break; + } + } + } + add_char (0); + return length - 1; +} diff --git a/src/kadmin/config.mk/ChangeLog b/src/kadmin/config.mk/ChangeLog new file mode 100644 index 000000000..5dd46d71a --- /dev/null +++ b/src/kadmin/config.mk/ChangeLog @@ -0,0 +1,10 @@ +Fri Jul 12 14:39:28 1996 Marc Horowitz + + * architecture: add uname test for NetBSD + * netbsd1.def: added + +Mon Jul 8 16:39:36 1996 Barry Jaspan + + * template, site.def, rules: Add SITEMAKEFILES to specify -f + Makefile.ov, add .ct default rule. + diff --git a/src/kadmin/config.mk/aix3.2.def b/src/kadmin/config.mk/aix3.2.def new file mode 100644 index 000000000..44fd07b1f --- /dev/null +++ b/src/kadmin/config.mk/aix3.2.def @@ -0,0 +1,40 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there is a blank space at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/bin/rsh +export INSTCMD = /usr/local/bin/ginstall -c +export LEX = /usr/local/bin/flex +export AWK_CMD = /usr/local/bin/gawk + +# needed for fd_set to be defined +# Not convinced! Experimenting with disabling _BSD. - jik 3/21/95 +ifndef KRB5B4 +CFLAGS += -D_BSD +endif +CFLAGS += -D_ALL_SOURCE + +PCC_STRUCT_RETURN = -fpcc-struct-return +D_NEEDS_RPCENT = -D_SUN + +D_NEEDS_GETUSERSHELL = -DNEEDS_GETUSERSHELL +D_NEEDS_TREEWALK = -DNEEDS_TREEWALK +D_SETEUID = -DNO_SETEUID -DSETEUID_INCLUDE='' \ + -DSETEUIDPRE='setuidx(ID_REAL|ID_EFFECTIVE,' -DSETEUIDPOST=')' +D_NO_SETENV = -DNO_SETENV +D_REGEXP_TYPE = -DPOSIX_REGEXPS + +TERMCAPLIB = -ltermcap + +# extra libraries needed for login +LOGINLIB = -ls -lcfg -lodm + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +XINCDIR = /usr/lpp/X11/include +XLIBDIR = /usr/lpp/X11/lib + +OMIT_DOC = true +DONT_STRIP_NXLIBS = true diff --git a/src/kadmin/config.mk/architecture b/src/kadmin/config.mk/architecture new file mode 100644 index 000000000..d976c27b2 --- /dev/null +++ b/src/kadmin/config.mk/architecture @@ -0,0 +1,68 @@ +# $Id$ + +# sample `uname -a` output: +# SunOS dun-dun-n 4.1.3 3 sun4c +# SunOS samosa 5.3 Generic_101318-31 sun4m sparc +# HP-UX strange- A.09.01 A 9000/710 2005970723 two-user license +# AIX krusty 2 3 000131533500 + +# each section here should set ARCH_OS to a symbol specifically describing +# the OS, and also set "relevant" capability symbols + +ifndef ARCH_OS +__UNAME_A := $(shell uname -a) +ifeq "$(strip $(filter SunOS 4.1.3, $(__UNAME_A)))" "SunOS 4.1.3" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.3C, $(__UNAME_A)))" "SunOS 4.1.3C" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.3_U1, $(__UNAME_A)))" "SunOS 4.1.3_U1" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.4, $(__UNAME_A)))" "SunOS 4.1.4" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 5.3, $(__UNAME_A)))" "SunOS 5.3" + export ARCH_OS := solaris2.3 +endif +# For now, assume that Solaris 2.4 is the same as Solaris 2.3. +ifeq "$(strip $(filter SunOS 5.4, $(__UNAME_A)))" "SunOS 5.4" + export ARCH_OS := solaris2.3 +endif +ifeq "$(strip $(filter HP-UX A.09.01, $(__UNAME_A)))" "HP-UX A.09.01" + export ARCH_OS := hpux9.01 +endif +ifeq "$(strip $(filter AIX 2, $(__UNAME_A)))" "AIX 2" + export ARCH_OS := aix3.2 +endif +ifeq "$(strip $(filter sweet-and-sour-sauce, $(__UNAME_A)))" "sweet-and-sour-sauce" + export ARCH_OS := aix3.2 + d:=$(shell echo "*** WARNING! Used hostname for architecture." 1>&2) +endif +ifeq "$(strip $(filter Linux, $(__UNAME_A)))" "Linux" + export ARCH_OS := linux +endif +ifeq "$(strip $(filter IRIX, $(__UNAME_A)))" "IRIX" + export ARCH_OS := irix5.2 +endif +ifeq "$(strip $(filter NetBSD, $(__UNAME_A)))" "NetBSD NetBSD" + export ARCH_OS := netbsd1 +endif + +ifndef ARCH_OS + d:=$(shell echo "*** WARNING! Unknown architecture: $(__UNAME_A)" 1>&2) + export ARCH_OS := dummy + export ARCH_OS_UNKNOWN := dummy +endif +endif + +ifndef ARCH_OS_UNKNOWN + define IncludeArchFile + include $(CONFDIR)/$(ARCH_OS).def + endef +else + define IncludeArchFile + endef +endif + diff --git a/src/kadmin/config.mk/config b/src/kadmin/config.mk/config new file mode 100644 index 000000000..9bb800e84 --- /dev/null +++ b/src/kadmin/config.mk/config @@ -0,0 +1,141 @@ +# $Id$ +# $Source$ + +# +# local Programs +# + +INSTHDRS = $(TOP)/scripts/inst-hdrs.sh +COMPILE_ET = $(TOP)/../util/et/compile_et +MK_CMDS = $(TOP)/../util/ss/mk_cmds +SYM_RANDOMIZE = $(TOP)/intl/sym-randomize.pl + +# +# Directories +# + +STAGETOP= $(TOP)/.. +STAGE_BINDIR = $(STAGETOP)/bin +STAGE_INCDIR = $(STAGETOP)/include +STAGE_LIBDIR = $(STAGETOP)/lib + +INSTALLTOP= $(TOP)/.. +INSTALL_BINDIR = $(INSTALLTOP)/bin +INSTALL_ETCDIR = $(INSTALLTOP)/etc +INSTALL_INCDIR = $(INSTALLTOP)/include +INSTALL_LIBDIR = $(INSTALLTOP)/lib +INSTALL_CONFDIR = $(INSTALLTOP)/config +INSTALL_PROTODIR = $(INSTALLTOP)/proto +INSTALL_ADMINDIR = $(INSTALLTOP)/admin +INSTALL_SERVERDIR = $(INSTALLTOP)/sbin +INSTALL_INSTDIR = $(INSTALLTOP)/install +INSTALL_DOCDIR = $(INSTALLTOP)/doc +INSTALL_MANDIR = $(INSTALLTOP)/man +INSTALL_SRCDIR = $(INSTALLTOP)/src + +# +# libraries +# + +# system +LIBM = -lm + +# stage +LIBADMCLNT := $(STAGE_LIBDIR)/libkadm5clnt.a +LIBADMSRV := $(STAGE_LIBDIR)/libkadm5srv.a +LIBRPCLIB := $(STAGE_LIBDIR)/libgssrpc.a +LIBDYN := $(STAGE_LIBDIR)/libdyn.a +LIBSS := $(STAGE_LIBDIR)/libss.a +LIBOVSEC_UTIL := $(STAGE_LIBDIR)/libkadm5_util.a +LIBFTPSEC := $(STAGE_LIBDIR)/libftpsec.a + +# install +LIBGSSAPI_KRB5 := $(INSTALL_LIBDIR)/libgssapi_krb5.a +LIBGSSAPI_KRB5_DX := $(INSTALL_LIBDIR)/libgssapi_krb5_dx.a +LIBGSSAPI_KRB5_WX := $(INSTALL_LIBDIR)/libgssapi_krb5_wx.a +LIBGSSAPI_KRB5_NX := $(INSTALL_LIBDIR)/libgssapi_krb5_nx.a +LIBGSSAPI_TRUST := $(INSTALL_LIBDIR)/libgssapi_trust.a +LIBDB := $(INSTALL_LIBDIR)/libdb.a +LIBKRB5 := $(INSTALL_LIBDIR)/libkrb5.a +LIBKDB5 := $(INSTALL_LIBDIR)/libkdb5.a +LIBCRYPTO := $(INSTALL_LIBDIR)/libcrypto.a +ifndef KRB5B4 +LIBISODE := $(INSTALL_LIBDIR)/libisode.a +endif +LIBCOM_ERR := $(INSTALL_LIBDIR)/libcom_err.a +LIBKRB5_ALL := $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR) +LIBKRB := $(INSTALL_LIBDIR)/libkrb4.a +LIBKDB := $(INSTALL_LIBDIR)/libkdb.a +LIBKADM := $(INSTALL_LIBDIR)/libkadm.a +LIBKRB425 := $(INSTALL_LIBDIR)/libkrb425.a +LIBDES425 := $(INSTALL_LIBDIR)/libdes425.a + +# X libraries. XXX this uses -L! but perhaps it doesn't matter, see +# [secure-build/2649] +XLIB = -L$(XLIBDIR) -lXext -lX11 + +# +# library name mangling for export +# + +ADMIN_INTERFACE_SYMBOL_FILES := $(TOP)/intl/adm-export-symbols \ + $(TOP)/intl/misc-export-symbols +OTHER_INTERFACE_SYMBOL_FILES := $(TOP)/intl/gss-export-symbols + +ADMIN_CRYPTO_LIBS := $(LIBADMCLNT) $(LIBADMSRV) $(LIBRPCLIB) \ + $(LIBGSSAPI_KRB5) $(LIBKRB5) $(LIBKDB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBKRB) $(LIBKDB) $(LIBDES425) +OTHER_CRYPTO_LIBS := $(LIBGSSAPI_KRB5_DX) $(LIBGSSAPI_KRB5_WX) \ + $(LIBGSSAPI_KRB5_NX) + +# +# compiler/linker flags +# + +CFLAGS := $(CFLAGS) -I$(INSTALL_INCDIR) + +# +# Variables for testing +# +# These are all exported because lots of test scripts (/bin/sh, perl, +# tcl) use them. +# + +export TESTDIR = $(TOP)/testing +export COMPARE_DUMP = $(TESTDIR)/scripts/compare_dump.pl +export FIX_CONF_FILES = $(TESTDIR)/scripts/fixup-conf-files.pl +export INITDB = $(TESTDIR)/scripts/init_db +export MAKE_KEYTAB = $(TESTDIR)/scripts/make-host-keytab.pl +export LOCAL_MAKE_KEYTAB= $(TESTDIR)/scripts/make-host-keytab.pl +export RESTORE_FILES = $(TESTDIR)/scripts/restore_files.sh +export SAVE_FILES = $(TESTDIR)/scripts/save_files.sh +export SIMPLE_DUMP = $(TESTDIR)/scripts/simple_dump.pl +export TCLUTIL = $(TESTDIR)/tcl/util.t +export BSDDB_DUMP = $(TESTDIR)/util/bsddb_dump +export CLNTTCL = $(TESTDIR)/util/ovsec_kadm_clnt_tcl +export SRVTCL = $(TESTDIR)/util/ovsec_kadm_srv_tcl +export QUALNAME = $(TOP)/inst-scripts/qualname + +export START_SERVERS = $(TESTDIR)/scripts/start_servers $(TEST_SERVER) +export START_SERVERS_LOCAL = $(TESTDIR)/scripts/start_servers_local + +export STOP_SERVERS = $(TESTDIR)/scripts/stop_servers $(TEST_SERVER) +export STOP_SERVERS_LOCAL = $(TESTDIR)/scripts/stop_servers_local + +export KRB5_CONFIG = /krb5/krb5.conf +export KRB5_KDC_PROFILE = /krb5/kdc.conf +export KRB5_KTNAME = /krb5/ovsec_adm.srvtab + +ifdef TEST_SERVER +MAKE_KEYTAB += -server $(TEST_SERVER) +ifdef TEST_PATH +MAKE_KEYTAB += -top $(TEST_PATH) +START_SERVERS += $(TEST_PATH) +STOP_SERVERS += $(TEST_PATH) +endif +endif + +export VERBOSE_TEST DEJALFLAGS + +# We're running low on disk space, so strip installed binaries +STRIP_INSTALLED = true diff --git a/src/kadmin/config.mk/hpux9.01.def b/src/kadmin/config.mk/hpux9.01.def new file mode 100644 index 000000000..17da13c52 --- /dev/null +++ b/src/kadmin/config.mk/hpux9.01.def @@ -0,0 +1,32 @@ +export ARCH_SKIP_RANLIB := 1 +export INSTCMD = ginstall -c +export PS_ALL = ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = ps -t +export STATIC_LINK_GUI := 1 +export RSH_CMD = /usr/bin/remsh + +PCC_STRUCT_RETURN = -fpcc-struct-return +# this must always be at the end. +REGEXLIB = -lc -lPW +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/include/X11R5 +XLIBDIR = /usr/lib/X11R5 +NDBMLIB := /usr/lib/libndbm.a +ARCH_CFLAGS = -mpa-risc-1-0 + +CFLAGS := $(ARCH_CFLAGS) $(CFLAGS) + +UTMP_FILE = /etc/utmp +WTMP_FILE = /etc/wtmp + +D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48 +D_SETEUID = -DNO_SETEUID \ + -DSETEUIDPRE='setresuid(-1,' -DSETEUIDPOST=',-1)' +D_FATALINLIBS = -Dfatal=ftpd_fatal +D_NO_SETENV = -DNO_SETENV +D_REGEXP_TYPE = -DPOSIX_REGEXPS + diff --git a/src/kadmin/config.mk/irix5.2.def b/src/kadmin/config.mk/irix5.2.def new file mode 100644 index 000000000..053d9328e --- /dev/null +++ b/src/kadmin/config.mk/irix5.2.def @@ -0,0 +1,22 @@ +export PS_ALL = /usr/bin/ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = /usr/bin/ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = /usr/bin/ps -t + +RANLIB = /bin/true +D_NO_SETENV = -DNO_SETENV +D_HAVE_SYSLOG_H = -DHAVE_SYSLOG_H +D_HAVE_STDARG_H = -DHAVE_STDARG_H +D_HAVE_SYSLOG = -DHAVE_SYSLOG +D_HAVE_VSPRINTF = -DHAVE_VSPRINTF +D_HAVE_OPENLOG = -DHAVE_OPENLOG +D_HAVE_CLOSELOG = -DHAVE_CLOSELOG +D_HAVE_STRFTIME = -DHAVE_STRFTIME + +# libgen.a is for regcmp and regex, used in /usr/lib/libXm.a; this is +# a different set of regexp routines than used by svr_iters.c +XM_LIB = /usr/lib/libXm.a /usr/lib/libgen.a +XT_LIB = /usr/lib/libXt.a +X_LIB = /usr/lib/libX11.so + diff --git a/src/kadmin/config.mk/linux.def b/src/kadmin/config.mk/linux.def new file mode 100644 index 000000000..69a0c6287 --- /dev/null +++ b/src/kadmin/config.mk/linux.def @@ -0,0 +1,23 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +export RSH_CMD = /usr/bin/rsh + +XINCDIR = /usr/include +XLIBDIR = /usr/lib + +NDBMLIB = -ldbm + +PERL = /usr/bin/perl +LEX_LIB = -lfl + +OMIT_GUI = true +OMIT_XM_KPASSWD = true +OMIT_DOC = true + +UTMP_FILE = /etc/utmp +WTMP_FILE = /var/adm/wtmp + +LIBTCL = -ltcl + +TERMCAPLIB = -ltermcap diff --git a/src/kadmin/config.mk/netbsd1.def b/src/kadmin/config.mk/netbsd1.def new file mode 100644 index 000000000..2b6090355 --- /dev/null +++ b/src/kadmin/config.mk/netbsd1.def @@ -0,0 +1,22 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/bin/rsh + +PCC_STRUCT_RETURN = +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/X11/include +XLIBDIR = /usr/X11/lib + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +D_NO_SETENV = -DNO_SETENV + +SHLIBCFLAGS := -fpic +SHLIBLDFLAGS := -assert pure-text +SHLIBEXT := so +SHLIBSEP := . diff --git a/src/kadmin/config.mk/rules b/src/kadmin/config.mk/rules new file mode 100644 index 000000000..35b647bda --- /dev/null +++ b/src/kadmin/config.mk/rules @@ -0,0 +1,538 @@ +# +# $Id$ +# $Source$ +# + +# +# DefaultRules --- clean, depend, all, stage, includes, and +# install. +# + +define DefaultRules +clean:: + $(CLEAN) core *.o *~ *.bak #* y.output + +depend:: + +all:: + +includes:: + +stage:: + +install:: + +unit-test:: + +traverse:: + +endef + + +# +# SubdirTarget +# +# SUBDIRS = subdirs to work in +# +define SubdirTarget +__SUBDIR_TARGET := clean +expand _DoSubdir + +__SUBDIR_TARGET := depend +expand _DoSubdir + +__SUBDIR_TARGET := all +expand _DoSubdir + +__SUBDIR_TARGET := includes +expand _DoSubdir + +__SUBDIR_TARGET := stage +expand _DoSubdir + +__SUBDIR_TARGET := install +expand _DoSubdir + +__SUBDIR_TARGET := unit-test +expand _DoSubdir + +__SUBDIR_TARGET := traverse +expand _DoSubdir + +ALL_SUBDIRS_TARGET := everything +expand AllTargetsTarget +endef + +# +# Makes the target $(ALL_SUBDIRS_TARGET) perform the targets includes +# depend stage all install in $(SUBDIRS). +# +define AllTargetsTarget +$(ALL_SUBDIRS_TARGET):: + @cwd=`pwd`; \ + for d in $(SUBDIRS); do \ + echo "--- Making includes depend stage all install in $(CUR_DIR)/$$$$d"; \ + cd $$$$d && \ + $(MAKE) includes depend CUR_DIR=$(CUR_DIR)/$$$$d; \ + $(MAKE) stage all install CUR_DIR=$(CUR_DIR)/$$$$d; \ + cd $$$$cwd; \ + done + +endef + +define _DoSubdir +# $(__SUBDIR_TARGET):: +# @echo "--- SKIPPING $(__SUBDIR_TARGET) in $(SUBDIRS)" +$(__SUBDIR_TARGET):: + @cwd=`pwd`; \ + for d in $(SUBDIRS); do \ + echo "--- Making $(__SUBDIR_TARGET) in $(CUR_DIR)/$$$$d"; \ + cd $$$$d && \ + $(MAKE) $(SITEMAKEFLAGS) $(__SUBDIR_TARGET) CUR_DIR=$(CUR_DIR)/$$$$d; \ + cd $$$$cwd; \ + done +endef + +# +# Program -- compile prog, adds all and clean targets +# +# PROG program name +# OBJS object files +# DEPS additional dependencies (e.g.: libraries) +# LIBS libraries +# LDFLAGS arguments for link stage +# PUREFLAGS arguments for purify +# PURELIBS extra libraries to link in when using purify +# +define Program +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).$(__PID__) + $(CC) $(LDFLAGS) -o /tmp/$(PROG).$(__PID__) $(OBJS) $(LIBS) $(STD_LIBS) + mv /tmp/$(PROG).$(__PID__) $(PROG) +else + $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS) $(STD_LIBS) +endif + +clean:: + $(CLEAN) $(PROG) + +proof:: $(PROG).tc + +$(PROG).tc: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).tc.$(__PID__) + $(PROOF) $(CC) $(LDFLAGS) -o /tmp/$(PROG).tc.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) + mv /tmp/$(PROG).tc.$(__PID__) $(PROG).tc +else + $(PROOF) $(CC) $(LDFLAGS) -o $(PROG).tc $(OBJS) $(LIBS) $(STD_LIBS) +endif + +clean:: + $(CLEAN) $(PROG).tc $(PROG).tc.* + +pure:: $(PROG).pure + +$(PROG).pure: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).pure.$(__PID__) + $(PURIFY) $(PUREFLAGS) \ + $(CC) $(LDFLAGS) -o /tmp/$(PROG).pure.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) $(PURELIBS) + mv /tmp/$(PROG).pure.$(__PID__) $(PROG).pure +else + $(PURIFY) $(PUREFLAGS) $(CC) $(LDFLAGS) -o $(PROG).pure $(OBJS) $(LIBS) $(STD_LIBS) $(PURELIBS) +endif + +clean:: + $(CLEAN) $(PROG).pure $(PROG).pure.pure_* + +quant:: $(PROG).quant + +$(PROG).quant: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).quant.$(__PID__) + $(QUANTIFY) $(QUANTFLAGS) \ + $(CC) $(LDFLAGS) -o /tmp/$(PROG).quant.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) $(QUANTLIBS) + mv /tmp/$(PROG).quant.$(__PID__) $(PROG).quant +else + $(QUANTIFY) $(QUANTFLAGS) $(CC) $(LDFLAGS) -o $(PROG).quant $(OBJS) $(LIBS) $(STD_LIBS) $(QUANTLIBS) +endif + +clean:: + $(CLEAN) $(PROG).quant $(PROG).quant.quant.*.qv +endef + +# +# InstallProgram -- build program, install in INSTALL_BINDIR, adds +# install target +# +define InstallProgram +__INST_DIR := $(INSTALL_BINDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallUtil -- build program, install in INSTALL_ETCDIR, adds +# install target +# +define InstallUtil +__INST_DIR := $(INSTALL_ETCDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallAdmin -- build program, install in INSTALL_ADMINDIR, adds +# install target +# +define InstallAdmin +__INST_DIR := $(INSTALL_ADMINDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallServer -- build program, install in INSTALL_SERVERDIR, adds +# install target +# +define InstallServer +__INST_DIR := $(INSTALL_SERVERDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallExecuteable(bin, as, dir) +# +define InstallExecutable +ifndef INST_NAME +install:: $(PROG) + rm -f $(__INST_DIR)/$(PROG) + $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(PROG) +else +install:: $(PROG) + rm -f $(__INST_DIR)/$(INST_NAME) + $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(INST_NAME) +endif +endef + +# +# Library -- create library from object files and ranlib it, adds all +# and clean targets +# +# LIB name of library (e.g. libfoo.a) +# OBJS object files in library +# +define Library +all:: $(LIB) + +$(LIB): $(OBJS) $(OTHER_OBJS) + $(RM) -f $(LIB) + $(AR) $(ARFLAGS) $(LIB) $(OBJS) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(LIB) +endif + +clean:: + $(CLEAN) $(LIB) +endef + +# +# SharedLibrary -- create shared library from object files, adds all +# and clean targets +# +# LIB basename of library (e.g. libfoo) +# (this will be basenamified, anyway) +# OBJS object files in library +# VERSION version of shared library +# +define SharedLibrary +ifdef DO_SHARED_LIBRARIES +ifdef SHLIBLDFLAGS +ifdef SHLIBSEP +__LIB := $(basename $(LIB)).$(SHLIBEXT)$(SHLIBSEP)$(VERSION) +else +__LIB := $(basename $(LIB)).$(SHLIBEXT) +endif +__LIBNV := $(basename $(LIB)).$(SHLIBEXT) +expand SharedLibrary_1 +endif +endif +endef + +define SharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +all:: $(__LIB) + +$(__LIB): $(OBJS) $(OTHER_OBJS) + $(RM) -f $(__LIB) $(__LIBNV) + $(LD) -o $(__LIB) $(SHLIBLDFLAGS) $(OBJS) + $(LNSOFT) $(__LIB) $(__LIBNV) +clean:: + $(CLEAN) $(__LIB) $(__LIBNV) +endif +endef + +# +# StageLibrary -- build library, install in STAGE_LIBDIR, adds stage target +# +define StageLibrary +expand Library + +stage:: $(STAGE_LIBDIR)/$(LIB) + +$(STAGE_LIBDIR)/$(LIB): $(LIB) + $(INSTCMD) $(LIB) $(STAGE_LIBDIR)/$(LIB) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(STAGE_LIBDIR)/$(LIB) +endif +endef + +# +# StageSharedLibrary -- build library, install in STAGE_LIBDIR, adds +# stage target +# +define StageSharedLibrary +ifdef DO_SHARED_LIBRARIES +expand SharedLibrary +expand StageSharedLibrary_1 +endif +endef + +define StageSharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +ifdef __LIB +stage:: $(STAGE_LIBDIR)/$(__LIB) + +$(STAGE_LIBDIR)/$(__LIB): $(__LIB) + $(INSTCMD) $(__LIB) $(STAGE_LIBDIR)/$(__LIB) +endif +endif +endef + +# +# InstallLibrary -- build library, install in INSTALL_LIBDIR, +# adds install target +# +define InstallLibrary +expand Library + +install:: $(INSTALL_LIBDIR)/$(LIB) + +$(INSTALL_LIBDIR)/$(LIB): $(LIB) + $(INSTCMD) $(LIB) $(INSTALL_LIBDIR)/$(LIB) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(INSTALL_LIBDIR)/$(LIB) +endif +endef + +# +# InstallSharedLibrary -- build library, install in INSTALL_LIBDIR, +# adds install target +# +define InstallSharedLibrary +ifdef DO_SHARED_LIBRARIES +expand SharedLibrary +expand InstallSharedLibrary_1 +endif +endef + +define InstallSharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +ifdef __LIB +install:: $(INSTALL_LIBDIR)/$(__LIB) + +$(INSTALL_LIBDIR)/$(__LIB): $(__LIB) + $(INSTCMD) $(__LIB) $(INSTALL_LIBDIR)/$(__LIB) +endif +endif +endef + +# +# StageFiles(files, dir) +# +define StageFiles +stage:: $(FILES) + $(INSTCMD) $(FILES) $(DIR) +endef + +# +# StageIncludes -- copy include files to staging area, adds includes +# and stage target +# +# HDRS header files to copy +# HDRS_DIR subdir of STAGE_INCDIR to copy to +# +define StageIncludes +includes:: $(HDRS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS) + +stage:: $(HDRS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS) +endef + +# +# InstallIncludes -- copy include files to staging area, adds includes +# and install target +# +# HDRS header files to copy +# HDRS_DIR subdir of INSTALL_INCDIR to copy to +# +define InstallIncludes +includes:: $(HDRS) + $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS) + +install:: $(HDRS) + $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS) +endef + +# +# Depend -- run makedepend +# +# SRCS source files to generate dependencies for +# DEPENDS dependencies for "make depend" +# ETABLES error tables whose .c and .h files are implicitly +# dependencies of make depend +# +define Depend +depend:: $(DEPENDS) $(addsuffix .c,$(basename $(ETABLES))) $(addsuffix .h,$(basename $(ETABLES))) + +ifeq (,$(findstring -a,$(__MDFLAGS))) + @rm -f Makefile.depend; touch Makefile.depend +endif + $(MAKEDEPEND) $(__MDFLAGS) $(MDFLAGS) -fMakefile.depend -- $(CFLAGS) -- $(SRCS) + +clean:: + $(CLEAN) Makefile.depend + +ifeq (,$(findstring -a,$(__MDFLAGS))) +__MDFLAGS += -a +endif +endef + +# +# NormalProgram -- everything for a single program +# +# PROG = program name +# SRCS = list of .c sources +# HDRS = list of .h sources +# OBJS = list of .o files to depend and link against +# STAGELIBS = foo.a within the stage area tree to link against +# INSTALLLIBS = foo.a within the install area tree to link against +# LIBS = system libraries to link against +# +# STAGELIBS And INSTALLLIBS are added to the dependencies for PROG. +# +define NormalProgram +expand SaveStuff +LIBS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \ + $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \ + $(LIBS) +DEPS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \ + $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \ + $(DEPS) + +clean:: + $(CLEAN) $(OBJS) + +expand InstallProgram +expand Depend +expand RestoreStuff +endef + +define NormalLibrary +expand Library + +clean:: + $(CLEAN) $(OBJS) + +expand Depend +endef + +# +# ErrorTables -- compile an error table with compile_et +# +# ETABLES list of .et files +# CFLAGS for saber target +# +# +define ErrorTables +__ETABLE_HS := $(addsuffix .h,$(basename $(ETABLES))) +__ETABLE_CS := $(addsuffix .c,$(basename $(ETABLES))) +expand ErrorTables_1 +endef + +define ErrorTables_1 + +saber:: + #load $(CFLAGS) $(__ETABLE_CS) + +clean:: + $(CLEAN) $(__ETABLE_HS) $(__ETABLE_CS) +endef + +# +# StageErrorTables -- copy generated include file into staging area. +# +# ETABLES list of .et file +# HDRS_DIR subdir of STAGE_INCDIR to copy to +# +define StageErrorTables +expand ErrorTables +expand StageErrorTables_1 +endef + +define StageErrorTables_1 +includes:: $(__ETABLE_HS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS) + +stage:: $(__ETABLE_HS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS) +endef + +define SaveStuff +__LIBS := $(LIBS) +__DEPS := $(DEPS) +endef + +define RestoreStuff +LIBS := $(__LIBS) +DEPS := $(__DEPS) +endef + +# +# XDR -- generate .c and .h from .x +# +# XDRS list of .x files +# +define XDR +__XDR_HS := $(addsuffix .h,$(basename $(XDRS))) +__XDR_CS := $(addsuffix .c,$(basename $(XDRS))) +expand XDR_1 +endef + +define XDR_1 +saber:: + #load $(CFLAGS) $(__XDR_CS) + +clean:: + $(CLEAN) $(__XDR_HS) $(__XDR_CS) +endef + + + +# +# Saber -- load files into saber +# +# SRCS list of .c files +# +define Saber +saber:: + #load $(CFLAGS) $(SRCS) +endef diff --git a/src/kadmin/config.mk/site.def b/src/kadmin/config.mk/site.def new file mode 100644 index 000000000..be7fe9e25 --- /dev/null +++ b/src/kadmin/config.mk/site.def @@ -0,0 +1,52 @@ +# $Id$ + +# XXXX this file will probably have lots of ARCH_OS defines in it. + +# +# Misc settings +# + +# +# site-specific compiler/linker flags +# + +CFLAGS := $(CFLAGS) -I$(TOP)/../../src/include \ + -I$(TOP)/../../src/include/krb5 \ + -I$(TOP)/../include \ + -I$(TOP)/../include/krb5 \ + $(PCC_STRUCT_RETURN) -g + +SITEMAKEFLAGS := -f Makefile.ov + +# +# Packages whose locations we need to know +# + +ifeq ($(shell ls -d /.afs/gza.com/product/secure 2>/dev/null),/.afs/gza.com/product/secure) +AFS_ROOT=/.afs +else +AFS_ROOT=/afs +endif + +PERL = /afs/athena/contrib/perl/p +# /afs/sipb/project/tcl/lib/libtcl.a +LIBTCL = /mit/gnu/lib/libtcl.a +# /afs/sipb/project/tcl/include +TCLINC = /mit/gnu/include + +# +# Default locations +# + +YACC = bison -y + +# +# I'm not really sure where this should go, but it is often useful to +# be able to set up a test environment from anywhere in the build +# tree. +# +start-servers: + $(START_SERVERS) + +stop-servers: + $(STOP_SERVERS) diff --git a/src/kadmin/config.mk/solaris2.3.def b/src/kadmin/config.mk/solaris2.3.def new file mode 100644 index 000000000..d18e0e164 --- /dev/null +++ b/src/kadmin/config.mk/solaris2.3.def @@ -0,0 +1,39 @@ +export PS_ALL = /usr/bin/ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = /usr/bin/ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = /usr/bin/ps -t +export RSH_CMD = /usr/ucb/rsh +export INSTCMD = /usr/ucb/install +export PATH := $(PATH):/usr/ucb:/usr/ccs/bin + +PCC_STRUCT_RETURN = -fpcc-struct-return +D_NEEDS_RPCENT = -DNEEDS_RPCENT +D_SYSV = -DSYSV +D_POSIX = -DPOSIX +D_NO_SETENV = -DNO_SETENV +D_POSIX_SIGNALS = -DPOSIX_SIGNALS +D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48 +D_REGEXP_TYPE = -DSOLARIS_REGEXPS +NETLIB = -lsocket -lnsl +#BSDLIB = /usr/ucblib/libucb.a +TERMCAPLIB = -lcurses -ltermcap +REGEXLIB = -lgen +LOGINLIB = -lcmd + +XINCDIR = /usr/openwin/include +XLIBDIR = /usr/openwin/lib +RANLIB = /bin/true + +OMIT_GUI = true + +# These are used by admin/v4server/Makefile. They are determined +# automatically by the krb5 beta 4 auto-configure, but we're not using +# that right now. +WAIT_USES_INT = true +OPEN_NEEDS_FCNTL = true + +UTMP_FILE = /var/adm/utmp +WTMP_FILE = /var/adm/wtmp +UTMPX_FILE = /var/adm/utmpx +WTMPX_FILE = /var/adm/wtmpx diff --git a/src/kadmin/config.mk/sunos4.1.def b/src/kadmin/config.mk/sunos4.1.def new file mode 100644 index 000000000..942615511 --- /dev/null +++ b/src/kadmin/config.mk/sunos4.1.def @@ -0,0 +1,22 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/ucb/rsh + +PCC_STRUCT_RETURN = -fpcc-struct-return +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/openwin/include +XLIBDIR = /usr/openwin/lib + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +D_NO_SETENV = -DNO_SETENV + +SHLIBCFLAGS := -fpic +SHLIBLDFLAGS := -assert pure-text +SHLIBEXT := so +SHLIBSEP := . diff --git a/src/kadmin/config.mk/template b/src/kadmin/config.mk/template new file mode 100644 index 000000000..caf3a2d9d --- /dev/null +++ b/src/kadmin/config.mk/template @@ -0,0 +1,142 @@ +# $Id$ +# $Source$ + +export TOP + +KRB5B4 = true +CONFDIR = $(TOP)/config.mk + +ifndef CUR_DIR +CUR_DIR = . +endif + +# +# get the os name +# + +include $(CONFDIR)/architecture + +# +# Programs +# +IMAKE = imake +# The purpose of this variable setting is to prevent -w from being +# passed down via environment variables into sub-makes that use SunOS +# Make rather than GNU make. +ifndef MAKE_PRINT_DIRECTORY +MAKE := $(MAKE) --no-print-directory MAKEFLAGS=$(MAKEFLAGS) MFLAGS=$(MFLAGS) +endif +CC = gcc +AR = ar +RANLIB = ranlib +LD = ld +RM = rm +CLEAN = rm -f +MV = mv +LN = ln +LNSOFT = $(LN) -s +MAKEDEPEND = makedepend +RPCGEN = rpcgen +PERL = /usr/local/bin/perl +DUMPPERL = /usr/local/bin/perl.static +UNDUMP = undump +YACC = $(TOP)/scripts/newyacc.sh +GENPROT = $(TOP)/scripts/genproto.sh +INSTCMD = install -c +export INSTCMD +PURIFY = purify +PROOF = proof +QUANTIFY = quantify +LEX_LIB = -ll +PERL = /usr/local/bin/perl +OBJDUMP = /usr/local/bin/gobjdump +OBJCOPY = /usr/local/bin/gobjcopy + +# Dejagnu variables + +# We have to set the host with --host so that setup_xfail will work. +# If we don't set it, then the host type used is "native", which +# doesn't match "*-*-*". + +DEJAFLAGS := $(DEJALFLAGS) $(CLFLAGS) --debug --host \ + unknown-$(shell uname -m)-$(shell uname -s)$(shell uname -r) +ifdef VERBOSE_TEST +DEJAFLAGS += --verbose +endif + +RUNTEST := runtest $(DEJAFLAGS) + +# +# Flags. Since this is the initial setting, don't preserve current +# values; otherwise, recursive makes will get the sum of everything. +# +YFLAGS = -d +ARFLAGS = cru +CFLAGS := $(CLFLAGS) +LDFLAGS := $(CLFLAGS) + +# +# The default target is "all". Put this before any includes, in case +# the includes define new targets. Or perhaps they should be allowed +# to define a new default target... +# +all:: + +# +# Get a unique number for files built in /tmp +# +__PID__ := $(shell echo $$$$) + +include $(CONFDIR)/config + +include $(CONFDIR)/rules + +expand IncludeArchFile + +include $(CONFDIR)/site.def + +ifdef STRIP_INSTALLED +INST_PROG_FLAGS = -s +endif + +# avoid makefiles from failing on default rules +expand DefaultRules + +# include dependencies +ifeq ($(shell [ -r Makefile.depend ] && echo yes),yes) +include Makefile.depend +endif + +# disable RCS frobbing +% :: RCS/%,v + +# fix lex rule +.l.c: + $(RM) -f $@ + $(LEX) $(LFLAGS) -t $< > $@ + +# error table rule +.SUFFIXES: .et +.et.c: + $(COMPILE_ET) $< +.et.h: + $(COMPILE_ET) $< + +# rpcgen rule +.SUFFIXES: .x +.x.c: + $(RPCGEN) -o $@ -c $< +.x.h: + $(RPCGEN) -o $@ -h $< + +# command table rule +.SUFFIXES: .ct +.ct.c: + $(MK_CMDS) $< +.ct.h: + $(MK_CMDS) $< + +CMD="echo 'You must specify CMD to use the 'execute' rule.'; exit 1" + +execute: + @eval $(CMD) diff --git a/src/kadmin/configure.in b/src/kadmin/configure.in index 12d4f04ef..959626b36 100644 --- a/src/kadmin/configure.in +++ b/src/kadmin/configure.in @@ -1,5 +1,6 @@ AC_INIT(configure.in) CONFIG_RULES -CONFIG_DIRS(kpasswd v5server v5client ktutil) +dnl CONFIG_DIRS(kpasswd v5server v5client) +CONFIG_DIRS(create export import keytab cli dbutil passwd ktutil server v4server) DO_SUBDIRS V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/ChangeLog b/src/kadmin/create/ChangeLog new file mode 100644 index 000000000..0c724422d --- /dev/null +++ b/src/kadmin/create/ChangeLog @@ -0,0 +1,9 @@ +Fri Jul 12 14:43:56 1996 Marc Horowitz + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require that + all symbols be resolved, even if they are not used by the + executeable. Thus, create needs to link against gssapi + +Wed Jul 10 01:24:29 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support diff --git a/src/kadmin/create/Makefile.in b/src/kadmin/create/Makefile.in new file mode 100644 index 000000000..547bd39c6 --- /dev/null +++ b/src/kadmin/create/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kdb5_create +OBJS = kdb5_create.o kadm5_create.o string_table.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/create/Makefile.ov b/src/kadmin/create/Makefile.ov new file mode 100644 index 000000000..cdec414b7 --- /dev/null +++ b/src/kadmin/create/Makefile.ov @@ -0,0 +1,12 @@ +TOP = .. +include $(TOP)/config.mk/template + +PROG = kadmin_create +SRCS = kdb5_create.c kadm5_create.c string_table.c +OBJS = kdb5_create.o kadm5_create.o string_table.o + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) \ + $(LIBDYN) $(NDBMLIB) $(LIBDB) $(BSDLIB) $(NETLIB) + +expand InstallAdmin +expand Depend diff --git a/src/kadmin/create/attic/Makefile.in b/src/kadmin/create/attic/Makefile.in new file mode 100644 index 000000000..f7bd9ca38 --- /dev/null +++ b/src/kadmin/create/attic/Makefile.in @@ -0,0 +1,20 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +all:: + +SRCS = $(srcdir)/ovsec_adm_create.c \ + $(srcdir)/string_table.c + +OBJS = ovsec_adm_create.o \ + string_table.o + +all:: ovsec_adm_create + +ovsec_adm_create: $(OBJS) $(DEPLIBS) + $(LD) $(LDFLAGS) $(LDARGS) -o ovsec_adm_create $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) ./ovsec_adm_create ${DESTDIR}$(SERVER_BINDIR)/kadmind5 + +clean:: + $(RM) ovsec_adm_create diff --git a/src/kadmin/create/attic/configure.in b/src/kadmin/create/attic/configure.in new file mode 100644 index 000000000..67b8f7c52 --- /dev/null +++ b/src/kadmin/create/attic/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_create.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +USE_DB_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/attic/make_extern b/src/kadmin/create/attic/make_extern new file mode 100644 index 000000000..5432edf66 --- /dev/null +++ b/src/kadmin/create/attic/make_extern @@ -0,0 +1,16 @@ +#!/bin/csh + +echo '/*' +echo ' * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.' +echo ' * ' +echo ' * $Header$' +echo ' *' +echo ' */' +echo ' ' +echo '#ifndef _OVSEC_ADM_STRINGS_' +echo ' ' + +cat $1 | grep -v rcsid | grep ^char | awk '{printf "extern %s %s;\n",$1,$2}' + +echo ' ' +echo '#endif /* _OVSEC_ADM_STRINGS_ */' diff --git a/src/kadmin/create/attic/ovsec_adm_create.c b/src/kadmin/create/attic/ovsec_adm_create.c new file mode 100644 index 000000000..90be0c406 --- /dev/null +++ b/src/kadmin/create/attic/ovsec_adm_create.c @@ -0,0 +1,663 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.23 1996/07/22 20:24:35 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.22.4.1 1996/07/18 03:01:22 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.22.2.1 1996/06/20 21:44:55 marc + * File added to the repository on a branch + * + * Revision 1.22 1996/06/19 15:09:32 bjaspan + * changes to work in mit tree + * + * Revision 1.21 1995/11/07 23:27:28 grier + * Add stdlib.h + * Add string.h + * + * Revision 1.20 1995/08/13 16:41:11 jik + * Fix a nonsensical comment about the iterator() function. See PR + * secure-admin/470. + * + * Revision 1.19 1995/07/02 19:55:13 jik + * Key version numbers should start out at 1, not 0. + * Should get the master key version number from the master_db entry in + * server_kdb.c, rather than assuming that the master key version number + * is 0. + * + * Revision 1.18 1995/03/14 16:58:50 jik + * Use krb5_xfree instead of xfree if KRB5B4 is defined. + * + * Revision 1.17 1994/03/11 19:37:34 bjaspan + * [secure-admin/1593: ovsec_adm_create non-error messages go to stderr] + * [secure-releng/1608: audit secure-admin/1593: ovsec_adm_create non-error messages go to stderr] + * + * Sandbox: + * + * Normal messages should be printed to stdout rather than displayed + * using com_err, which will cause then to go to stderr. + * + * Revision 1.17 1994/03/09 22:21:33 jik + * Normal messages should be printed to stdout rather than displayed + * using com_err, which will cause then to go to stderr. + * + * Revision 1.16 1993/12/21 20:26:34 marc + * create new principals with policy NULL, not "" + * + * Revision 1.15 1993/12/14 22:51:35 marc + * missing * in call to krb5_random_key + * + * Revision 1.14 1993/11/27 20:42:32 bjaspan + * fix secure/621: coredumps with default realm + * + * Revision 1.13 1993/11/19 20:03:51 shanzer + * osa_adb_open_T takes a file name argument. + * + * Revision 1.12 1993/11/10 21:30:24 bjaspan + * move init code to main, accept -m + * + * Revision 1.11 1993/11/10 04:33:35 bjaspan + * rewrote adding principals to kdb, and set lifetimes + * + * Revision 1.10 1993/11/06 00:08:44 bjaspan + * use new OVSEC_KADM_* names, use correct realm + * + * Revision 1.9 1993/11/05 05:05:35 bjaspan + * added -r realm argument + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include +#include +#include +#include +#include + +#include +#include + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: main + * + * Purpose: create admin principals, create and populate admin dbs + * + * Arguments: + * + * input none + * exit status 1 for error 0 for success + * + * Requires: + * + * + * Effects: + * + * + * Modifies: + * + */ + +void usage() +{ + fprintf(stderr, "%s\n", str_PROG_CREATE_USAGE); + exit(1); +} + +void main(int argc, char **argv) +{ + char *realm = NULL; + int freerealm = 0; + int retval, from_keyboard = 0; + krb5_principal creator = NULL; + void *handle; + krb5_context context; + + whoami = str_PROG_NAME_CREATE; + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-r") == 0) { + argc--; argv++; + if (!argc) + usage(); + realm = *argv; + } else if (strcmp(*argv, "-m") == 0) { + from_keyboard = 1; + } else + break; + argc--; argv++; + } + + if (argc != 0) + usage(); + + if (retval = krb5_init_context(&context)) + exit(ERR); + + if (realm == NULL) { + if ((retval = krb5_get_default_realm(context, &realm)) != 0) + exit(retval); + freerealm = 1; + } + + if ((retval = ovsec_kadm_init(whoami, from_keyboard?"non-null":NULL, + NULL, realm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, realm); + + ovsec_kadm_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + exit(0); +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates OVSEC_KADM_ADMIN_SERVICE, + * OVSEC_KADM_CHANGEPW_SERVICE, and OVSEC_KADM_HIST_PRINCIPAL. If any + * of these exist a message is printed. If any of these existing + * principal do not have the proper attributes, a warning message is + * printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +#if 0 + /* this is now done inside kdb_init_hist in the admin server */ + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_HIST_PRINCIPAL, realm, + KRB5_KDB_DISALLOW_ALL_TIX, + 0))) + goto clean_and_exit; +#endif + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + ovsec_kadm_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = ovsec_kadm_create_principal(handle, &ent, + (OVSEC_KADM_PRINCIPAL | + OVSEC_KADM_MAX_LIFE | + OVSEC_KADM_ATTRIBUTES), + "to-be-random")) { + if (ret == OVSEC_KADM_DUP) + ret = ovsec_kadm_modify_principal(handle, &ent, + (OVSEC_KADM_PRINCIPAL | + OVSEC_KADM_MAX_LIFE | + OVSEC_KADM_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = ovsec_kadm_randkey_principal(handle, ent.principal, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} + +#if 0 +/* + * Function: main + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +krb5_error_code add_random_princ(princ_str, princ, attrs, lifetime, + creator, rseed) + char *princ_str; + krb5_principal princ, creator; + krb5_flags attrs; + int lifetime; + krb5_pointer *rseed; +{ + krb5_db_entry entry; + krb5_error_code ret; + krb5_encrypted_keyblock ekey; + krb5_keyblock *rkey; + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + entry.principal = princ; + entry.kvno = 1; + entry.max_life = KRB5_KDB_MAX_LIFE; + entry.max_renewable_life = 0; + entry.mkvno = master_db.mkvno; + entry.expiration = KRB5_KDB_EXPIRATION; + entry.mod_name = creator; + if (lifetime != 0) + entry.max_life = lifetime; + + if (ret = krb5_timeofday(&entry.mod_date)) + return(ret); + + entry.attributes = attrs; + + ret = krb5_random_key(&master_encblock, *rseed, &rkey); + if (ret != 0) { + com_err(whoami, ret, str_RANDOM_KEY, princ_str); + return (ERR); + } + + + ret = krb5_kdb_encrypt_key(&master_encblock, rkey, &ekey); + krb5_free_keyblock(rkey); + if (ret != 0) { + com_err(whoami, ret, str_ENCRYPT_KEY, princ_str); + return (ERR); + } + + entry.key = ekey; + entry.salt_type = KRB5_KDB_SALTTYPE_NORMAL; + entry.salt_length = 0; + entry.salt = 0; + + ret = krb5_db_put_principal(&entry, &nentries); + if (ret != 0) + com_err(whoami, ret, str_PUT_PRINC, princ_str); +#ifdef KRB5B4 + krb5_xfree(ekey.contents); +#else + xfree(ekey.contents); +#endif + + if (ret) return(ERR); + + printf(str_CREATED_PRINC, whoami, princ_str); + + return(OK); +} + +/* + * Function: create_admin_policy_db + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +int create_admin_policy_db() +{ + /* We don't have a create/destroy routine, so opening the db and + closing it will have to do. */ + osa_adb_policy_t policy_db = NULL; + osa_adb_ret_t ret; + + ret = osa_adb_open_policy(&policy_db, POLICY_DB); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CREATING_POLICY_DB); + return(-1); + } + + /* Should create sample policies here */ + + ret = osa_adb_close_policy(policy_db); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CLOSING_POLICY_DB); + return(-1); + } + + printf(str_CREATED_POLICY_DB, whoami); + + return(OK); +} + +/* + + * Function: iterator(ptr, entry) + * + * Purpose: + * + * Creates an entry in the Admin database corresponding to the + * specified entry in the Kerberos database. + * + * Arguments: + * + * ptr (input) Actually of type osa_adb_princ_t, + * represents the Admin database in which to + * create the principal. + * entry (input) The entry in the Kerberos database for + * which to create an entry in the Admin + * database. + * + * Requires: + * + * "ptr" represents a valid, open Admin principal database. + * "entry" represents a valid, decoded Kerberos database + * principal entry. + * + * Effects: + * + * Modifies the Admin principal database by creating a principal + * in the database with the same name as "entry" and no other + * information. + * + * Modifies: + * + * Does not modify any global memory. Modifies the Admin + * principal database whose handle is passed into it. + */ + +krb5_error_code +iterator(ptr, entry) +krb5_pointer ptr; +krb5_db_entry *entry; +{ + osa_adb_ret_t retval; + krb5_error_code retval2; + char *princ_str = NULL; + osa_princ_ent_rec osa_princ; + + /* Zero the whole struct, and fill in the princ name */ + memset(&osa_princ, 0, sizeof(osa_princ_ent_rec)); + + osa_princ.name = entry->principal; + osa_princ.policy = NULL; + + retval = osa_adb_create_princ((osa_adb_princ_t) ptr, &osa_princ); + if (retval != OSA_ADB_OK) { + if (retval2 = krb5_unparse_name(entry->principal, &princ_str)) { + com_err(whoami, retval2, str_UNPARSE_PRINC); + } + com_err(whoami, retval, str_CREATING_PRINC_ENTRY, + (princ_str ? princ_str : str_A_PRINC)); + if (princ_str) free(princ_str); + } + return (0); +} + +/* + * Function: create_and_populate_admin_princ_db + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +int create_and_populate_admin_princ_db() +{ + osa_adb_princ_t princ_db = NULL; + osa_adb_ret_t ret; + + /* We don't have a create/destroy routine, so opening the db and + closing it will have to do. */ + + ret = osa_adb_open_princ(&princ_db, PRINCIPAL_DB); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CREATING_PRINC_DB); + return(-1); + } + + printf(str_CREATED_PRINC_DB, whoami); + + (void) krb5_db_iterate(iterator, princ_db); + + ret = osa_adb_close_princ(princ_db); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CLOSING_PRINC_DB); + return(-1); + } + + + return(OK); +} + +#endif diff --git a/src/kadmin/create/configure.in b/src/kadmin/create/configure.in new file mode 100644 index 000000000..403034263 --- /dev/null +++ b/src/kadmin/create/configure.in @@ -0,0 +1,11 @@ +AC_INIT(kdb5_create.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/kadm5_create.c b/src/kadmin/create/kadm5_create.c new file mode 100644 index 000000000..33b30ec9c --- /dev/null +++ b/src/kadmin/create/kadm5_create.c @@ -0,0 +1,241 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include +#include +#include +#include +#include + +#include +#include + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +extern char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: kadm5_create + * + * Purpose: create admin principals in KDC database + * + * Arguments: params (r) configuration parameters to use + * + * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE + * principals in the KDC database and sets their attributes + * appropriately. + */ +void kadm5_create(kadm5_config_params *params) +{ + int retval; + void *handle; + krb5_context context; + FILE *f; + + + if (retval = krb5_init_context(&context)) + exit(ERR); + + /* + * The lock file has to exist before calling kadm5_init, but + * params->admin_lockfile may not be set yet... + */ + if (retval = kadm5_get_config_params(context, NULL, NULL, + params, params)) { + com_err(whoami, retval, str_INITING_KCONTEXT); + exit(1); + } + + if (retval = osa_adb_create_policy_db(params)) { + com_err(whoami, retval, str_CREATING_POLICY_DB); + exit(1); + } + + if ((retval = kadm5_init(whoami, NULL, NULL, params, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, params->realm); + + kadm5_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + exit(0); +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates KADM5_ADMIN_SERVICE, + * KADM5_CHANGEPW_SERVICE. If any of these exist a message is + * printed. If any of these existing principal do not have the proper + * attributes, a warning message is printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + KADM5_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + kadm5_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = kadm5_create_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES), + "to-be-random")) { + if (ret == KADM5_DUP) + ret = kadm5_modify_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} diff --git a/src/kadmin/create/kdb5_create.c b/src/kadmin/create/kdb5_create.c new file mode 100644 index 000000000..8b167b6b9 --- /dev/null +++ b/src/kadmin/create/kdb5_create.c @@ -0,0 +1,536 @@ +/* + * admin/create/kdb5_create.c + * + * Copyright 1990,1991 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. + * + * + * Generate (from scratch) a Kerberos KDC database. + */ + +#include +#include +#include + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + TGT_KEY /* special handling for tgt key */ +}; + +krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; + krb5_int32 nkslist; + krb5_key_salt_tuple *kslist; +} rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + (krb5_encrypt_block *) NULL, + (krb5_pointer) NULL, + 1, + &def_kslist +}; + +struct iterate_args { + krb5_context ctx; + struct realm_info *rblock; + krb5_db_entry *dbentp; +}; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +/* + * Steps in creating a database: + * + * 1) use the db calls to open/create a new database + * + * 2) get a realm name for the new db + * + * 3) get a master password for the new db; convert to an encryption key. + * + * 4) create various required entries in the database + * + * 5) close & exit + */ + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-d dbpathname] [-r realmname] [-k enctype]\n\ +\t[-M mkeyname]\n", + who); + exit(status); +} + +krb5_keyblock master_keyblock; +krb5_principal master_princ; +krb5_encrypt_block master_encblock; +krb5_data master_salt; + +krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +char *mkey_password = 0; +char *whoami; + +void +main(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; + int optchar; + + krb5_error_code retval; + char *dbname = (char *) NULL; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *defrealm; + char *pw_str = 0; + char *keyfile = 0; + int pw_size = 0; + int enctypedone = 0; + int do_stash = 0; + krb5_data pwd; + krb5_context context; + krb5_realm_params *rparams; + kadm5_config_params kadm5_params; + + memset(&kadm5_params, 0, sizeof(kadm5_params)); + + krb5_init_context(&context); + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + whoami = argv[0]; + + kadm5_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + kadm5_params.mkey_from_kbd = 1; + + while ((optchar = getopt(argc, argv, "d:r:k:M:e:P:sf:")) != EOF) { + switch(optchar) { + case 'd': /* set db name */ + kadm5_params.dbname = dbname = optarg; + kadm5_params.mask |= KADM5_CONFIG_DBNAME; + break; + case 'r': + kadm5_params.realm = realm = optarg; + kadm5_params.mask |= KADM5_CONFIG_REALM; + break; + case 'k': + if (!krb5_string_to_enctype(optarg, &master_keyblock.enctype)){ + enctypedone++; + kadm5_params.enctype = master_keyblock.enctype; + kadm5_params.mask |= KADM5_CONFIG_ENCTYPE; + } + else + com_err(argv[0], 0, "%s is an invalid enctype", optarg); + break; + case 's': + do_stash++; + kadm5_params.mkey_from_kbd = 0; + break; + case 'f': + kadm5_params.stash_file = keyfile = optarg; + kadm5_params.mask |= KADM5_CONFIG_STASH_FILE; + break; + case 'M': /* master key name in DB */ + kadm5_params.mkey_name = mkey_name = optarg; + kadm5_params.mask |= KADM5_CONFIG_MKEY_NAME; + break; + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + break; + case '?': + default: + usage(argv[0], 1); + /*NOTREACHED*/ + } + } + + /* + * Attempt to read the KDC profile. If we do, then read appropriate values + * from it and augment values supplied on the command line. + */ + if (!(retval = krb5_read_realm_params(context, + realm, + (char *) NULL, + (char *) NULL, + &rparams))) { + /* Get the value for the database */ + if (rparams->realm_dbname && !dbname) + dbname = strdup(rparams->realm_dbname); + + /* Get the value for the master key name */ + if (rparams->realm_mkey_name && !mkey_name) + mkey_name = strdup(rparams->realm_mkey_name); + + /* Get the value for the master key type */ + if (rparams->realm_enctype_valid && !enctypedone) { + master_keyblock.enctype = rparams->realm_enctype; + enctypedone++; + } + + /* Get the value for maximum ticket lifetime. */ + if (rparams->realm_max_life_valid) + rblock.max_life = rparams->realm_max_life; + + /* Get the value for maximum renewable ticket lifetime. */ + if (rparams->realm_max_rlife_valid) + rblock.max_rlife = rparams->realm_max_rlife; + + /* Get the value for the default principal expiration */ + if (rparams->realm_expiration_valid) + rblock.expiration = rparams->realm_expiration; + + /* Get the value for the default principal flags */ + if (rparams->realm_flags_valid) + rblock.flags = rparams->realm_flags; + + /* Get the value of the supported key/salt pairs */ + if (rparams->realm_num_keysalts) { + rblock.nkslist = rparams->realm_num_keysalts; + rblock.kslist = rparams->realm_keysalts; + rparams->realm_num_keysalts = 0; + rparams->realm_keysalts = (krb5_key_salt_tuple *) NULL; + } + + /* Get the value for the stash file */ + if (rparams->realm_stash_file && !keyfile) + keyfile = strdup(rparams->realm_stash_file); + + krb5_free_realm_params(context, rparams); + } + + if (!dbname) + dbname = DEFAULT_KDB_FILE; + + if (!enctypedone) + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + retval = krb5_db_set_name(context, dbname); + if (!retval) retval = EEXIST; + + if (retval == EEXIST || retval == EACCES || retval == EPERM) { + /* it exists ! */ + com_err(argv[0], 0, "The database '%s' appears to already exist", + dbname); + exit(1); + } + if (!realm) { + if ((retval = krb5_get_default_realm(context, &defrealm))) { + com_err(argv[0], retval, "while retrieving default realm name"); + exit(1); + } + realm = defrealm; + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ))) { + com_err(argv[0], retval, "while setting up master key name"); + exit(1); + } + + krb5_princ_set_realm_data(context, &db_create_princ, realm); + krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm)); + krb5_princ_set_realm_data(context, &tgt_princ, realm); + krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm)); + krb5_princ_component(context, &tgt_princ,1)->data = realm; + krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + dbname, realm, mkey_fullname); + + if (!mkey_password) { + printf("You will be prompted for the database Master Password.\n"); + printf("It is important that you NOT FORGET this password.\n"); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + + retval = krb5_read_password(context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(argv[0], retval, "while reading master key from keyboard"); + exit(1); + } + mkey_password = pw_str; + } + + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(context, master_princ, &master_salt); + if (retval) { + com_err(argv[0], retval, "while calculated master key salt"); + exit(1); + } + if (retval = krb5_string_to_key(context, &master_encblock, + &master_keyblock, &pwd, &master_salt)) { + com_err(argv[0], retval, "while transforming master key from password"); + exit(1); + } + + if ((retval = krb5_process_key(context, &master_encblock, + &master_keyblock))) { + com_err(argv[0], retval, "while processing master key"); + exit(1); + } + + rblock.eblock = &master_encblock; + if ((retval = krb5_init_random_key(context, &master_encblock, + &master_keyblock, &rblock.rseed))) { + com_err(argv[0], retval, "while initializing random key generator"); + (void) krb5_finish_key(context, &master_encblock); + exit(1); + } + if ((retval = krb5_db_create(context, dbname))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while creating database '%s'", + dbname); + exit(1); + } + if ((retval = krb5_db_set_name(context, dbname))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while setting active database to '%s'", + dbname); + exit(1); + } + if ((retval = krb5_db_init(context))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while initializing the database '%s'", + dbname); + exit(1); + } + + if ((retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) || + (retval = add_principal(context, &tgt_princ, TGT_KEY, &rblock))) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while adding entries to the database"); + exit(1); + } + if (do_stash && + ((retval = krb5_db_store_mkey(context, keyfile, master_princ, + &master_keyblock)))) { + com_err(argv[0], errno, "while storing key"); + printf("Warning: couldn't stash master key.\n"); + } + /* clean up */ + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + free(master_keyblock.contents); + if (pw_str) { + memset(pw_str, 0, pw_size); + free(pw_str); + } + free(master_salt.data); + + kadm5_create(&kadm5_params); + + exit(0); + +} + +static krb5_error_code +tgt_keysalt_iterate(ksent, ptr) + krb5_key_salt_tuple *ksent; + krb5_pointer ptr; +{ + krb5_context context; + krb5_error_code kret; + struct iterate_args *iargs; + krb5_keyblock random_keyblock, *key; + krb5_int32 ind; + krb5_encrypt_block random_encblock; + krb5_pointer rseed; + krb5_data pwd; + + iargs = (struct iterate_args *) ptr; + kret = 0; + + context = iargs->ctx; + + /* + * Convert the master key password into a key for this particular + * encryption system. + */ + krb5_use_enctype(context, &random_encblock, ksent->ks_enctype); + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock, + &pwd, &master_salt)) + return kret; + if ((kret = krb5_init_random_key(context, &random_encblock, + &random_keyblock, &rseed))) + return kret; + + if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { + ind = iargs->dbentp->n_key_data-1; + if (!(kret = krb5_random_key(context, + &random_encblock, rseed, + &key))) { + kret = krb5_dbekd_encrypt_key_data(context, + iargs->rblock->eblock, + key, + NULL, + 1, + &iargs->dbentp->key_data[ind]); + krb5_free_keyblock(context, key); + } + } + memset((char *)random_keyblock.contents, 0, random_keyblock.length); + free(random_keyblock.contents); + (void) krb5_finish_random_key(context, &random_encblock, &rseed); + return(kret); +} + +static krb5_error_code +add_principal(context, princ, op, pblock) + krb5_context context; + krb5_principal princ; + enum ap_op op; + struct realm_info *pblock; +{ + krb5_error_code retval; + krb5_db_entry entry; + + krb5_timestamp now; + struct iterate_args iargs; + + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.attributes = pblock->flags; + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.expiration = pblock->expiration; + + if ((retval = krb5_copy_principal(context, princ, &entry.princ))) + goto error_out; + + if ((retval = krb5_timeofday(context, &now))) + goto error_out; + + if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, + now, &db_create_princ))) + goto error_out; + + switch (op) { + case MASTER_KEY: + if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) + == NULL) + goto error_out; + memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); + entry.n_key_data = 1; + + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, NULL, + 1, entry.key_data))) + return retval; + break; + case TGT_KEY: + iargs.ctx = context; + iargs.rblock = pblock; + iargs.dbentp = &entry; + /* + * Iterate through the key/salt list, ignoring salt types. + */ + if ((retval = krb5_keysalt_iterate(pblock->kslist, + pblock->nkslist, + 1, + tgt_keysalt_iterate, + (krb5_pointer) &iargs))) + return retval; + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_db_put_principal(context, &entry, &nentries); + +error_out:; + krb5_dbe_free_contents(context, &entry); + return retval; +} diff --git a/src/kadmin/create/string_table.c b/src/kadmin/create/string_table.c new file mode 100644 index 000000000..b9f86a363 --- /dev/null +++ b/src/kadmin/create/string_table.c @@ -0,0 +1,91 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* String table of messages for kadm5_create */ + +char *str_INITING_KCONTEXT = "while initializing the kerberos context"; + +char *str_PARSE_NAME = "while parsing admin principal name."; + +char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name."; + +char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists."; + +char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists."; + +char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists."; + +char *str_ADMIN_PRINC_WRONG_ATTRS = + "Warning! Admin principal has incorrect attributes.\n" + "\tDISALLOW_TGT should be set, and max_life should be three hours.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_CHANGEPW_PRINC_WRONG_ATTRS = + "Warning! Changepw principal has incorrect attributes.\n" + "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and " + "max_life should be five minutes.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_HISTORY_PRINC_WRONG_ATTRS = + "Warning! Admin history principal has incorrect attributes.\n" + "\tDISALLOW_ALL_TIX should be set.\n" + "\tThis program will leave it as-is, but beware!."; + +char *str_CREATED_PRINC_DB = + "%s: Admin principal database created (or it already existed).\n"; /* whoami */ + +char *str_CREATED_POLICY_DB = + "%s: Admin policy database created (or it already existed).\n"; /* whoami */ + +char *str_RANDOM_KEY = + "while calling random key for %s."; /* principal name */ + +char *str_ENCRYPT_KEY = + "while calling encrypt key for %s."; /* principal name */ + +char *str_PUT_PRINC = + "while calling storing %s in Kerberos database."; /* principal name */ + +char *str_CREATING_POLICY_DB = "while creating/opening admin policy database."; + +char *str_CLOSING_POLICY_DB = "while closing admin policy database."; + +char *str_CREATING_PRINC_DB = "while creating/opening admin principal database."; + +char *str_CLOSING_PRINC_DB = "while closing admin principal database."; + +char *str_CREATING_PRINC_ENTRY = + "while creating admin principal database entry for %s."; /* princ_name */ + +char *str_A_PRINC = "a principal"; + +char *str_UNPARSE_PRINC = "while unparsing principal."; + +char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */ + +char *str_INIT_KDB = "while initializing kdb."; + +char *str_NO_KDB = +"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\ +If you haven't run kdb5_create you need to do so before running this command."; + + +char *str_INIT_RANDOM_KEY = "while initializing random key generator."; + +char *str_TOO_MANY_ADMIN_PRINC = + "while fetching admin princ. Can only have one admin principal."; + +char *str_TOO_MANY_CHANGEPW_PRINC = + "while fetching changepw princ. Can only have one changepw principal."; + +char *str_TOO_MANY_HIST_PRINC = + "while fetching history princ. Can only have one history principal."; + +char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets."; diff --git a/src/kadmin/create/string_table.h b/src/kadmin/create/string_table.h new file mode 100644 index 000000000..e8cb45367 --- /dev/null +++ b/src/kadmin/create/string_table.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + */ + +#ifndef _OVSEC_ADM_STRINGS_ + +extern char *str_INITING_KCONTEXT; +extern char *str_PARSE_NAME; +extern char *str_HISTORY_PARSE_NAME; +extern char *str_ADMIN_PRINC_EXISTS; +extern char *str_CHANGEPW_PRINC_EXISTS; +extern char *str_HISTORY_PRINC_EXISTS; +extern char *str_ADMIN_PRINC_WRONG_ATTRS; +extern char *str_CHANGEPW_PRINC_WRONG_ATTRS; +extern char *str_HISTORY_PRINC_WRONG_ATTRS; +extern char *str_CREATED_PRINC_DB; +extern char *str_CREATED_POLICY_DB; +extern char *str_RANDOM_KEY; +extern char *str_ENCRYPT_KEY; +extern char *str_PUT_PRINC; +extern char *str_CREATING_POLICY_DB; +extern char *str_CLOSING_POLICY_DB; +extern char *str_CREATING_PRINC_DB; +extern char *str_CLOSING_PRINC_DB; +extern char *str_CREATING_PRINC_ENTRY; +extern char *str_A_PRINC; +extern char *str_UNPARSE_PRINC; +extern char *str_CREATED_PRINC; +extern char *str_INIT_KDB; +extern char *str_NO_KDB; +extern char *str_INIT_RANDOM_KEY; +extern char *str_TOO_MANY_ADMIN_PRINC; +extern char *str_TOO_MANY_CHANGEPW_PRINC; +extern char *str_TOO_MANY_HIST_PRINC; +extern char *str_WHILE_DESTROYING_ADMIN_SESSION; + +#endif /* _OVSEC_ADM_STRINGS_ */ diff --git a/src/kadmin/dbutil/ChangeLog b/src/kadmin/dbutil/ChangeLog new file mode 100644 index 000000000..7719cc749 --- /dev/null +++ b/src/kadmin/dbutil/ChangeLog @@ -0,0 +1,440 @@ +Thu Jul 18 19:22:04 1996 Marc Horowitz + + * configure.in: removed SS_RULES + +Wed Jul 10 19:43:22 1996 Marc Horowitz + + * dumpv4.c (configure.in, Makefile.in): make autoconf work after + barry's carnage + +Sun May 12 00:27:44 1996 Marc Horowitz + + * loadv4.c (enter_in_v5_db, add_principal), kdb5_edit.c + (create_db_entry, modent), dumpv4.c (dump_v4_iterator), dump.c + (dump_k5beta_iterator, process_k5beta_record): convert to use new + krb5_dbe_* tl_data functions. + + * cpw.c (enter_pwd_key): krb5_dbe_cpw() takes a kvno now. + +Tue May 7 23:16:57 1996 Marc Horowitz + + * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY + +Thu Apr 11 19:32:36 1996 Richard Basch + + * kdb5_edit.c (extract_v4_srvtab): Use the matching key_data's kvno; + don't assume that key_data[0]'s kvno is necessarily the matching + key_data's kvno. + +Wed Apr 10 19:17:58 1996 Richard Basch + + * kdb5_edit.c (extract_v4_srvtab): Translate the principal name to + the common V4 name. + +Tue Mar 19 18:00:58 1996 Richard Basch + + * kdb5_edit.c (extract_v4_srvtab): do not test to make sure we + fetched a key of enctype 1 (des-cbc-crc), since we may have gotten + another des key from the database, which is just as useful in a + v4 srvtab + + * dumpv4.c (dump_v4_iterator): use krb5_524_conv_principal to do the + v5 to v4 principal translation, instead of having yet another + hard-coded table. + +Wed Mar 6 16:17:20 1996 Richard Basch + + * dumpv4.c: The V4 master key & schedule was never initialized, + so the dump created by dump_v4db was garbage. Read the V4 + master key from /.k or prompt for the V4 master key password. + If there is no V4-salt key in the database, but there is a DES + key, include it in the V4 dump, in case it is merely a random + service key for which there is no associated password. + Skip over K/M in the V5 database (use the entered V4 master key). + Both krbtgt and afs keys often have domain-qualifed instances. + +Tue Mar 5 12:18:22 1996 Richard Basch + + * dump.c: POSIX locking requires that the file be opened read-write. + +Mon Feb 26 22:42:09 1996 Mark Eichin + + * kdb5_edit.c: new command line option -f stashfile. + * kdb5_edit.M: document stashfile option. + +Mon Feb 26 22:13:45 1996 Mark Eichin + + * dump.c (process_k5beta_record): since V4 salt type has no data + either, only set key_data_ver to 1 for data_type 0 with 0-length + salt. Also, don't include alternate key if akey has all-zero type + and length in both fields. + +Sat Feb 24 04:02:18 1996 Mark W. Eichin + + * dump.c (process_k5beta_record): encrypted keys used to have 4 + byte lengths in MSB order, need to convert to 2 byte LSB order + lengths before storing. Handle primary key and alternate key. + +Fri Feb 23 18:44:10 1996 Mark Eichin + + * kdb5_edit.c (kdb5_edit_Init): set manual_mkey for testing with -P + +Wed Feb 14 09:52:18 1996 Ezra Peisach + + * kdb5_edit.c (enter_master_key, set_dbname_help): If master key + enctype is unknown, set to DEFAULT_KDC_ENCTYPE. + +Tue Feb 13 16:08:07 1996 Ezra Peisach + + * kdb5_edit.c (extract_v4_srvtab): krb5_dbekd_decrypt_key_data + takes krb5_key_data *, not **. + +Tue Jan 30 18:28:57 1996 Mark Eichin + + * dump.c (load_db): dbrenerr_fmt prints "from" first, so pass it + to fprintf correctly. + +Sun Jan 28 14:31:47 1996 Mark Eichin + + * dump.c (process_k5_record): t2..t9 is only 8 vars, not 9. + +Thu Jan 25 16:07:42 1996 Sam Hartman + + * kdb5_edit.c (extract_srvtab): Extract *all* the keys in a + dbentry, not the first one. + (extract_v4_srvtab): Attempt to find the right v4 keys. + +Wed Jan 24 18:48:38 1996 Tom Yu + + * Makefile.in: Remove spurious @DEFS@ + + +Wed Dec 13 03:44:58 1995 Chris Provenzano (proven@mit.edu) + + * dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : + Remove mkvno from krb5_db_entry. + +Sun Dec 10 11:07:51 1995 Ezra Peisach + + * kdb5_edit.M: Document that modent exists + + * kdb5_edit.c (modent): Add usage as suggested by jhawk@mit.edu. + +Thu Nov 09 17:05:57 1995 Chris Provenzano (proven@mit.edu) + + * kdb5_edit.c : Remove krb5_enctype from krb5_string_to_key() args. + +Fri Oct 27 13:37:04 1995 Ezra Peisach + + * dump.c (process_k5_record): Fix off by one in malloc. + +Mon Oct 9 16:35:19 1995 Ezra Peisach + + * kdb5_edit.c (extract_v4_srvtab): Extract a one byte version + number for v4 srvtabs (from warlord). + +Thu Oct 5 10:35:35 1995 Ezra Peisach + + * cpw.c: Declare std_ks_tuple as extern. + * kdb5_edit.h: Remove std_ks_tuple declaration as not all sources + include adm.h for structures + +Tue Oct 3 23:10:57 1995 Theodore Y. Ts'o + + * cpw.c (enter_rnd_key, enter_pwd_key): + * kdb5_edit.c (kdb5_edit_Init): Use the kdc.conf file to determine + the default list of keysalt tuples to be used. This is + stored in std_ks_tuple, and is used by cpw.c for random + keys and when a list of keysalts is not specified. + +Mon Sep 18 03:59:47 1995 Ezra Peisach + + * kdb5_edit.c (show_principal): Show key version and last password + change. + + * cpw.c: Fix typo in below change in which list was terminated + after third entry. (extra } removed) + +Fri Sep 15 14:21:25 1995 Theodore Y. Ts'o + + * cpw.c: Add DES_CBC_MD5 and DES_CBC_CRC with the V4 salt as + default key/salt tuples to be added. (Once proven's DES_* + folding code is implemented, we can shorten this list.) + Eventually, this list should be read in from kdc.conf. + +Thu Sep 7 20:41:24 1995 Ezra Peisach + + * loadv4.c (load_v4db): Provide a dummy routine if krb4 + compatibility is not compiled in. + +Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : + s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g + +Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : Remove krb5_enctype + references, and replace with krb5_keytype where appropriate. + +Fri Aug 25 17:37:33 EDT 1995 Paul Park (pjpark@mit.edu) + * dumpv4.c - Fix handle_keys(). It was trying to recreate work that + has already been done. + * Makefile.in, .Sanitize, loadv4.c, kdb5_ed_ct.ct - Add lddb4, the + command to load a v4 dump file. This is basically, kdb5_ + convert reconstituted to fit within the framework of kdb5_edit. + +Thu Aug 24 19:28:39 1995 Theodore Y. Ts'o + + * .Sanitize: Update file list + +Mon Aug 21 16:45:39 EDT 1995 Paul Park (pjpark@mit.edu) + * dump.c - Completely rework this logic to support old (e.g. Beta 5 + and previous) dump format and new dump format using the same + commands. This is differentiated by using the "-old" command + qualifier. + + * kdb5_edit.M - Add description of -R and -s. Remove "ascii represen- + tation of a decimal number". Remove "Bugs". + +Fri Aug 18 17:06:06 EDT 1995 Paul Park (pjpark@mit.edu) + + * ss_wrapper.c - Change sense of fgets() check so scripts work. + + +Tue Aug 15 14:22:50 EDT 1995 Paul Park (pjpark@mit.edu) + + * kdb5_edit.c, ss_wrapper.c, cpw.c, kdb5_edit.h - Add support for + -s scriptfile and fix up assorted gcc -Wall complaints. + + +Mon Aug 7 17:32:31 EDT 1995 Paul Park (pjpark@mit.edu) + * cpw.c - Use krb5_string_to_keysalts() to generate a list of unique + key/salt pairs supplied in argv. + + +Mon Aug 07 11:16:03 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c : Uses new kdb change password routines for ank, ark, cpw, + and crk. Also remove v4 variants of ank and cpw. + * krb5_edit.c : Deleted old variants of rotuines now in cpw.c + * kdb5_ed_ct.ct, kdb5_edit.M, tcl_wrapper.c: + Removed references to v4 variants of ank and cpw. + * kdb5_edit.h (enter_pwd_key()) : Removed proto, it's nolonger + necessary as it's a static routine in cpw.c + +Thu Aug 03 12:13:50 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c : New change password code for kdb5_edit. + * dumpv4.c : Get it to compile with new kdb format. + +Mon Jul 31 15:47:30 EDT 1995 Paul Park (pjpark@mit.edu) + * kdb5_edit.c - Use libkadm string conversion routines. These are + shared by all utilities. + * Makefile.in - Remove getdate.y. + * configure.in - Remove getdate.y dependency checks. + * getdate.y - Sayonara. + + +Thu Jul 27 15:01:01 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Add --with-dbm and check for already checking for dbm. + + +Thu Jul 27 02:59:05 1995 Chris Provenzano (proven@mit.edu) + + * dump.c kdb5_edit.c kdb5_edit.h util.c : Use new kdb format. + +Mon Jul 17 15:00:08 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Add KADM library. + * dumpv4.c - Change calling sequence to krb5_db_fetch_mkey(). + * kdb5_edit.c - Change calling sequence to krb5_db_fetch_mkey() which + uses the stash file. Add KDC profile reading/handling as a + supplement to command line supplied arguments. + + +Wed Jul 12 12:01:04 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Temporarily add --with-kdb4 option. Default is without + kdb4. Without kdb4 enables a define. With kdb4 uses -lkdb4 and + -l[n]dbm libraries. + * dumpv4.c - Conditionalize references to kdb4 routines with + KDB4_DISABLE. Replace two required routines: + kdb_encrypt_key -> pcbc_encrypt + kdb_get_master_key -> des_read_password/printf/key_sched + + +Fri Jul 7 15:38:00 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Remove all explicit library handling and LDFLAGS. + * configure.in - Add USE_ and KRB5_LIBRARIES. + + +Thu Jun 15 15:34:59 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Change explicit library names to -l form, and + change target link line to use $(LD) and associated flags. + Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these wer + split out. + * configure.in - Add shared library usage check. + +Fri Jun 9 18:14:43 1995 + + * configure.in: Remove standardized set of autoconf macros, which + are now handled by CONFIG_RULES. + + * dumpv4.c: Change name of controlling #ifdef to be + KRB5_KRB4_COMPAT instead of KRB4. + +Sun May 21 14:20:32 1995 Ezra Peisach + + * dumpv4.c: Include k5-int.h before krb.h so that PROTOTYPE is not + redefined. + +Sun May 7 13:46:30 1995 Ezra Peisach + + * configure.in: Add AC_HEADER_STDC to define STDC_HEADERS for + getdate.y. + +Mon May 1 13:36:41 1995 Theodore Y. Ts'o (tytso@dcl) + + * kdb5_edit.c (kdb5_edit_Init): Check the return code from + kdb5_init_context(). + +Fri Apr 28 18:04:26 1995 Mark Eichin + + * Makefile.in (LOCAL_LIBRARIES): put KRB4_LIB inside KLIB, and put + KDB4_LIB ahead of them both. + +Thu Apr 27 13:47:23 1995 Mark Eichin + + * Makefile.in (LOCAL_LIBRARIES): use KRB4_LIB and KDB4_LIB + directly. + * configure.in: just use WITH_KRB4. + +Wed Apr 19 13:59:47 1995 Ezra Peisach + + * kdb5_edit.c (kdb5_edit_Init): If a default realm is specified + (with -r), use krb5_set_default_realm so that created keys + will have the correct realm. + +Thu Mar 23 23:28:26 1995 Theodore Y. Ts'o + + * kdb5_edit.c (show_principal, parse_princ_args): Add + "support_desmd5" flag. + +Tue Mar 14 16:29:05 1995 + + * ss_wrapper.c (main): Set the return code from ss_execute_line(), + so that appropriate error checking is done. + +Thu Mar 2 12:18:57 1995 Theodore Y. Ts'o + + * Makefile.in (ISODELIB): Remove reference to $(ISODELIB). + +Wed Mar 1 11:53:02 1995 Theodore Y. Ts'o + + * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket + and -lnsl with WITH_NETLIB check. + +Tue Feb 28 02:06:26 1995 John Gilmore (gnu at toad.com) + + * dump.c, dumpv4.c, kdb5_edit.c, ss_wrapper.c, tcl_wrapper.c, + util.c: Avoid includes. + +Thu Feb 23 19:52:35 1995 Mark Eichin (eichin@cygnus.com) + + * kdb5_edit.c: add struct timeb and sys/timeb includes from + getdate.y. + (ftime): new function, in case we don't HAVE_FTIME. + +Tue Feb 14 17:55:47 1995 Tom Yu (tlyu@dragons-lair) + + * kdb5_edit.c: add modent + * getdate.y: import get_date + * kdbt_ed_ct.ct: add modent + * configure.in: + * Makefile.in: support for getdate.y + +Wed Feb 8 20:08:36 1995 Tom Yu (tlyu@dragons-lair) + + * kdb5_edit.c (show_principal): make sane and print all useful + fields + +Wed Jan 25 16:54:40 1995 Chris Provenzano (proven@mit.edu) + + * Removed all narrow types and references to wide.h and narrow.h + +Fri Jan 13 15:23:47 1995 Chris Provenzano (proven@mit.edu) + + * Added krb5_context to all krb5_routines + +Mon Dec 19 18:04:11 1994 Theodore Y. Ts'o (tytso@dcl) + + * configure.in: + * Makefile.in: + * dumpv4.c (dump_v4db): Do the right thing if we are compiling + without V4 support. (The dump_v4db command is disabled.) + +Wed Dec 7 00:07:46 1994 + + * dumpv4.c (v4_print_time): gmtime expects a pointer to a time_t, + not a long. On most systems these are the same, on + others.... + +Wed Nov 16 01:03:42 1994 Mark Eichin (eichin@cygnus.com) + + * dumpv4.c: new file. New command dump_v4db which creates a v4 + slave dump out of a v5 database, leaving out any keys which aren't + using v4 salt, and any keys that aren't for the current + realm. Reencrypts using v4 master key, synthesizes arbitrary + master key version number. + * configure.in: use WITH_KRB4 for dump support. + * kdb5_ed_ct.ct: add new dump_v4 command. + * Makefile.in: link in dumpv4. + +Fri Oct 14 23:31:49 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (load_db): When scanning a database entry, read + fail_auth_count into a temporary integer variable, and + then copy that into entry.fail_auth_count, which is a + char. + +Fri Oct 7 00:01:40 1994 Theodore Y. Ts'o (tytso@dcl) + + * kdb5_edit.c (kdb5_edit_Init): Don't let errors in + set_dbname_help initially cause the exit status to be set. + Commands like load_db don't need a valid database to be + opened. + + * ss_wrapper.c (main): Clear code before ss_execute_line, since + ss_execute_line doesn't set code to 0 if there are no + problems. + + * kdb5_edit.c (kdb5_edit_Init): Add a new option so that the + master key password can be entered on the command line --- + for testing only; not documented!! + +Mon Oct 3 19:10:47 1994 Theodore Y. Ts'o (tytso@dcl) + + * Makefile.in: Use $(srcdir) to find manual page for make install. + +Thu Sep 29 15:52:22 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (update_ok_file): Make sure mod time on the dump_ok file + is updated. (Some systems don't update the mod-time when + a file is opened for writing.) + + * Makefile.in: Relink executable when libraries change. + + * kdb5_edit.c (show_principal): Pass variable with correct type to + ctime(). + + * tcl_wrapper.c (doquit): + ss_wrapper.c (main): + kdb5_edit.c: + dump.c: Exit with a non-zero exit status if there was an error + in a executed command. + +Thu Sep 15 11:00:30 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (load_db): Fix error string on failed fopen. ("for + writing" -> "for reading") + + diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in new file mode 100644 index 000000000..b5bba09d9 --- /dev/null +++ b/src/kadmin/dbutil/Makefile.in @@ -0,0 +1,18 @@ +CFLAGS = $(CCOPTS) $(DEFS) -DKDB4_DISABLE $(LOCALINCLUDE) @KRB4_INCLUDES@ + +PROG = kdb5_util +OBJS = kdb5_create.o kadm5_create.o string_table.o +OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \ + kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \ + kdb5_destroy.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/dbutil/Makefile.ov b/src/kadmin/dbutil/Makefile.ov new file mode 100644 index 000000000..f762282f2 --- /dev/null +++ b/src/kadmin/dbutil/Makefile.ov @@ -0,0 +1,19 @@ +TOP = .. +include $(TOP)/config.mk/template + +CFLAGS += -I$(TOP)/../include/kerberosIV + +PROG = kdb5_util +SRCS = kdb5_util.c kdb5_util_ct.c dump.c ss_wrapper.c dumpv4.c loadv4.c \ + kdb5_create.c kadm5_create.c string_table.c kdb5_stash.c \ + kdb5_destroy.c +OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \ + kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \ + kdb5_destroy.o +ETABLES = kdb5_util_ct.ct + +LIBS = $(LIBADMSRV) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBRPCLIB) $(LIBDYN) \ + $(LIBSS) $(LIBDB) + +expand NormalProgram +expand ErrorTables diff --git a/src/kadmin/dbutil/configure.in b/src/kadmin/dbutil/configure.in new file mode 100644 index 000000000..c9234524e --- /dev/null +++ b/src/kadmin/dbutil/configure.in @@ -0,0 +1,13 @@ +AC_INIT(kdb5_create.c) +CONFIG_RULES +WITH_KRB4 +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +USE_SS_LIBRARY +USE_KRB4_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c new file mode 100644 index 000000000..8bd54ca4d --- /dev/null +++ b/src/kadmin/dbutil/dump.c @@ -0,0 +1,1957 @@ +/* + * admin/edit/dump.c + * + * Copyright 1990,1991 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. + * + * + * Dump a KDC database + */ + +#include +#include +#include +#include +#include +#include "kdb5_util.h" +#if HAVE_REGEX_H +#include +#endif /* HAVE_REGEX_H */ + +/* + * Use compile(3) if no regcomp present. + */ +#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H) +#define INIT char *sp = instring; +#define GETC() (*sp++) +#define PEEKC() (*sp) +#define UNGETC(c) (--sp) +#define RETURN(c) return(c) +#define ERROR(c) +#define RE_BUF_SIZE 1024 +#include +#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context kcontext; + char **names; + int nnames; + int verbose; +}; + +static krb5_error_code dump_k5beta_iterator PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static krb5_error_code dump_k5beta6_iterator PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static krb5_error_code dump_k5beta7_princ PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static void dump_k5beta7_policy PROTOTYPE((void *, osa_policy_ent_t)); + +typedef krb5_error_code (*dump_func)PROTOTYPE((krb5_pointer, + krb5_db_entry *)); + +static int process_k5beta_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +static int process_k5beta6_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +static int process_k5beta7_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +typedef krb5_error_code (*load_func)PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); + +typedef struct _dump_version { + char *name; + char *header; + dump_func dump_princ; + osa_adb_iter_policy_func dump_policy; + load_func load_record; +} dump_version; + +dump_version old_version = { + "Kerberos version 5 old format", + "kdb5_edit load_dump version 2.0\n", + dump_k5beta_iterator, + NULL, + process_k5beta_record, +}; +dump_version beta6_version = { + "Kerberos version 5 beta 6 format", + "kdb5_edit load_dump version 3.0\n", + dump_k5beta6_iterator, + NULL, + process_k5beta6_record, +}; +dump_version beta7_version = { + "Kerberos version 5", + "kdb5_util load_dump version 4\n", + dump_k5beta7_princ, + dump_k5beta7_policy, + process_k5beta7_record, +}; + +/* External data */ +extern char *current_dbname; +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; +extern kadm5_config_params global_params; + +/* Strings */ + +static const char k5beta_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5beta6_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char k5beta7_dump_header[] = "kdb5_edit load_dump version 4\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char stand_fmt_name[] = "Kerberos version 5"; +static const char old_fmt_name[] = "Kerberos version 5 old format"; +static const char b6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta_fmt_name[] = "Kerberos version 5 old format"; +static const char standard_fmt_name[] = "Kerberos version 5 format"; +static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname [admin_dbname]\n"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; + +static const char oldoption[] = "-old"; +static const char b6option[] = "-b6"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char dump_tmptrail[] = "~"; + +/* + * Update the "ok" file. + */ +void update_ok_file (file_name) + char *file_name; +{ + /* handle slave locking/failure stuff */ + char *file_ok; + int fd; + static char ok[]=".dump_ok"; + + if ((file_ok = (char *)malloc(strlen(file_name) + strlen(ok) + 1)) + == NULL) { + com_err(progname, ENOMEM, + "while allocating filename for update_ok_file"); + exit_status++; + return; + } + strcpy(file_ok, file_name); + strcat(file_ok, ok); + if ((fd = open(file_ok, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + com_err(progname, errno, "while creating 'ok' file, '%s'", + file_ok); + exit_status++; + free(file_ok); + return; + } + if (write(fd, "", 1) != 1) { + com_err(progname, errno, "while writing to 'ok' file, '%s'", + file_ok); + exit_status++; + free(file_ok); + return; + } + + free(file_ok); + close(fd); + return; +} + +/* + * name_matches() - See if a principal name matches a regular expression + * or string. + */ +static int +name_matches(name, arglist) + char *name; + struct dump_args *arglist; +{ +#if HAVE_REGCOMP + regex_t match_exp; + regmatch_t match_match; + int match_error; + char match_errmsg[BUFSIZ]; + size_t errmsg_size; +#elif HAVE_REGEXP_H + char regexp_buffer[RE_BUF_SIZE]; +#elif HAVE_RE_COMP + extern char *re_comp(); + char *re_result; +#endif /* HAVE_RE_COMP */ + int i, match; + + /* + * Plow, brute force, through the list of names/regular expressions. + */ + match = (arglist->nnames) ? 0 : 1; + for (i=0; innames; i++) { +#if HAVE_REGCOMP + /* + * Compile the regular expression. + */ + if (match_error = regcomp(&match_exp, + arglist->names[i], + REG_EXTENDED)) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + fprintf(stderr, regex_err, arglist->programname, match_errmsg); + break; + } + /* + * See if we have a match. + */ + if (match_error = regexec(&match_exp, name, 1, &match_match, 0)) { + if (match_error != REG_NOMATCH) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + fprintf(stderr, regex_merr, + arglist->programname, match_errmsg); + break; + } + } + else { + /* + * We have a match. See if it matches the whole + * name. + */ + if ((match_match.rm_so == 0) && + (match_match.rm_eo == strlen(name))) + match = 1; + } + regfree(&match_exp); +#elif HAVE_REGEXP_H + /* + * Compile the regular expression. + */ + compile(arglist->names[i], + regexp_buffer, + ®exp_buffer[RE_BUF_SIZE], + '\0'); + if (step(name, regexp_buffer)) { + if ((loc1 == name) && + (loc2 == &name[strlen(name)])) + match = 1; + } +#elif HAVE_RE_COMP + /* + * Compile the regular expression. + */ + if (re_result = re_comp(arglist->names[i])) { + fprintf(stderr, regex_err, arglist->programname, re_result); + break; + } + if (re_exec(name)) + match = 1; +#else /* HAVE_RE_COMP */ + /* + * If no regular expression support, then just compare the strings. + */ + if (!strcmp(arglist->names[i], name)) + match = 1; +#endif /* HAVE_REGCOMP */ + if (match) + break; + } + return(match); +} + +static krb5_error_code +find_enctype(dbentp, enctype, salttype, kentp) + krb5_db_entry *dbentp; + krb5_enctype enctype; + krb5_int32 salttype; + krb5_key_data **kentp; +{ + int i; + int maxkvno; + krb5_key_data *datap; + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i=0; in_key_data; i++) { + if ((dbentp->key_data[i].key_data_type[0] == enctype) && + ((dbentp->key_data[i].key_data_type[1] == salttype) || + (salttype < 0))) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + } + } + if (maxkvno >= 0) { + *kentp = datap; + return(0); + } + return(ENOENT); +} + +/* + * dump_k5beta_header() - Make a dump header that is recognizable by Kerberos + * Version 5 Beta 5 and previous releases. + */ +static krb5_error_code +dump_k5beta_header(arglist) + struct dump_args *arglist; +{ + /* The old header consists of the leading string */ + fprintf(arglist->ofile, k5beta_dump_header); + return(0); +} + +/* + * dump_k5beta_iterator() - Dump an entry in a format that is usable + * by Kerberos Version 5 Beta 5 and previous + * releases. + */ +static krb5_error_code +dump_k5beta_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name, *mod_name; + krb5_principal mod_princ; + krb5_tl_data *pwchg; + krb5_key_data *pkey, *akey, nullkey; + krb5_timestamp mod_date, last_pwd_change; + int i; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + mod_name = (char *) NULL; + memset(&nullkey, 0, sizeof(nullkey)); + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + /* + * Deserialize the modifier record. + */ + mod_name = (char *) NULL; + mod_princ = NULL; + last_pwd_change = mod_date = 0; + pkey = akey = (krb5_key_data *) NULL; + if (!(retval = krb5_dbe_lookup_mod_princ_data(arg->kcontext, + entry, + &mod_date, + &mod_princ))) { + if (mod_princ) { + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + mod_princ, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + krb5_free_principal(arg->kcontext, mod_princ); + } + } + if (!mod_name) + mod_name = strdup(null_mprinc_name); + + /* + * Find the last password change record and set it straight. + */ + if (retval = + krb5_dbe_lookup_last_pwd_change(arg->kcontext, entry, + &last_pwd_change)) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + krb5_xfree(name); + return(retval); + } + + /* + * Find the 'primary' key and the 'alternate' key. + */ + if ((retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_NORMAL, + &pkey)) && + (retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + &akey))) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + krb5_xfree(name); + return(retval); + } + + /* If we only have one type, then ship it out as the primary. */ + if (!pkey && akey) { + pkey = akey; + akey = &nullkey; + } + else { + if (!akey) + akey = &nullkey; + } + + /* + * First put out strings representing the length of the variable + * length data in this record, then the name and the primary key type. + */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), + strlen(mod_name), + (krb5_int32) pkey->key_data_length[0], + (krb5_int32) akey->key_data_length[0], + (krb5_int32) pkey->key_data_length[1], + (krb5_int32) akey->key_data_length[1], + name, + (krb5_int32) pkey->key_data_type[0]); + for (i=0; ikey_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); + } + /* + * Second, print out strings representing the standard integer + * data in this record. + */ + fprintf(arg->ofile, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", + (krb5_int32) pkey->key_data_kvno, + entry->max_life, entry->max_renewable_life, + 1 /* Fake mkvno */, entry->expiration, entry->pw_expiration, + last_pwd_change, entry->last_success, entry->last_failed, + entry->fail_auth_count, mod_name, mod_date, + entry->attributes, pkey->key_data_type[1]); + + /* Pound out the salt data, if present. */ + for (i=0; ikey_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); + } + /* Pound out the alternate key type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); + for (i=0; ikey_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); + } + /* Pound out the alternate salt type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); + for (i=0; ikey_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); + } + /* Pound out the expansion data. (is null) */ + for (i=0; i < 8; i++) { + fprintf(arg->ofile, "\t%u", 0); + } + fprintf(arg->ofile, ";\n"); + /* If we're blabbing, do it */ + if (arg->verbose) + fprintf(stderr, "%s\n", name); + krb5_xfree(mod_name); + } + krb5_xfree(name); + return(0); +} + +/* + * dump_k5beta6_iterator() - Output a dump record in krb5b6 format. + */ +static krb5_error_code +dump_k5beta6_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + krb5_tl_data *tlp; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length ] + * n_key_data*[ver kvno ver*(type length )] + * + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct. + */ + counter = 0; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) + counter++; + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + (int) entry->len, + strlen(name), + (int) entry->n_tl_data, + (int) entry->n_key_data, + (int) entry->e_length, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->expiration, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; itl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; countern_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; ikey_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; jkey_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (entry->e_length) + for (i=0; ie_length; i++) + fprintf(arg->ofile, "%02x", entry->e_data[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + fprintf(stderr, "%s\n", name); + } + else { + fprintf(stderr, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + } + krb5_xfree(name); + return(retval); +} + +/* + * dump_k5beta7_iterator() - Output a dump record in krb5b7 format. + */ +static krb5_error_code +dump_k5beta7_princ(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + int tmp_nnames; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + fprintf(arg->ofile, "princ\t"); + + /* save the callee from matching the name again */ + tmp_nnames = arg->nnames; + arg->nnames = 0; + retval = dump_k5beta6_iterator(ptr, entry); + arg->nnames = tmp_nnames; + } + + free(name); + return retval; +} + +void dump_k5beta7_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg; + + arg = (struct dump_args *) data; + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); +} + +/* + * usage is: + * dump_db [-old] [-b6] [-verbose] [filename [principals...]] + */ +void +dump_db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_args arglist; + int error; + char *programname; + char *ofile; + krb5_error_code kret; + dump_version *dump; + int aindex; + krb5_boolean locked; + extern osa_adb_policy_t policy_db; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + ofile = (char *) NULL; + error = 0; + dump = &beta7_version; + arglist.verbose = 0; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) + dump = &old_version; + else if (!strcmp(argv[aindex], b6option)) + dump = &beta6_version; + else if (!strcmp(argv[aindex], verboseoption)) + arglist.verbose++; + else + break; + } + + arglist.names = (char **) NULL; + arglist.nnames = 0; + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + if (aindex < argc) { + arglist.names = &argv[aindex]; + arglist.nnames = argc - aindex; + } + } + + /* + * Attempt to open the database. The policy database only has to + * be opened if we try a dump that uses it. + */ + if (!dbactive || (dump->dump_policy != NULL && policy_db == NULL)) { + com_err(argv[0], 0, Err_no_database); + exit_status++; + return; + } + + kret = 0; + locked = 0; + if (ofile && strcmp(ofile, "-")) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependencies than I + * want to get into. + */ + unlink(ofile); + if (!(f = fopen(ofile, "w"))) { + fprintf(stderr, ofopen_error, + programname, ofile, error_message(errno)); + exit_status++; + } + if ((kret = krb5_lock_file(util_context, + fileno(f), + KRB5_LOCKMODE_EXCLUSIVE))) { + fprintf(stderr, oflock_error, + programname, ofile, error_message(kret)); + exit_status++; + } + else + locked = 1; + } else { + f = stdout; + } + if (f && !(kret)) { + arglist.programname = programname; + arglist.ofile = f; + arglist.kcontext = util_context; + fprintf(arglist.ofile, "%s", dump->header); + if ((kret = krb5_db_iterate(util_context, + dump->dump_princ, + (krb5_pointer) &arglist))) { + fprintf(stderr, dumprec_err, + programname, dump->name, error_message(kret)); + exit_status++; + } + if (dump->dump_policy && + (kret = osa_adb_iter_policy(policy_db, dump->dump_policy, + &arglist))) { + fprintf(stderr, dumprec_err, programname, dump->name, + error_message(kret)); + exit_status++; + } + if (ofile && !exit_status) { + fclose(f); + update_ok_file(ofile); + } + } + if (locked) + (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); +} + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; itl_data; + (pwchg) && (pwchg->tl_data_type != KRB5_TL_LAST_PWD_CHANGE); + pwchg = pwchg->tl_data_next); + + /* Check to see if we found one. */ + linked = 0; + if (!pwchg) { + /* No, allocate a new one */ + if ((pwchg = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { + memset(pwchg, 0, sizeof(krb5_tl_data)); + if (!(pwchg->tl_data_contents = + (krb5_octet *) malloc(sizeof(krb5_timestamp)))) { + free(pwchg); + pwchg = (krb5_tl_data *) NULL; + } + else { + pwchg->tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + pwchg->tl_data_length = + (krb5_int16) sizeof(krb5_timestamp); + } + } + } + else + linked = 1; + + /* Do we have an entry? */ + if (pwchg && pwchg->tl_data_contents) { + /* Encode it */ + krb5_kdb_encode_int32(last_pwd_change, pwchg->tl_data_contents); + /* Link it in if necessary */ + if (!linked) { + pwchg->tl_data_next = dbentp->tl_data; + dbentp->tl_data = pwchg; + dbentp->n_tl_data++; + } + } + else + kret = ENOMEM; + } + + return(kret); +} +#endif + +/* + * process_k5beta_record() - Handle a dump record in old format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int nmatched; + int retval; + krb5_db_entry dbent; + int name_len, mod_name_len, key_len; + int alt_key_len, salt_len, alt_salt_len; + char *name; + char *mod_name; + int tmpint1, tmpint2, tmpint3; + int error; + const char *try2read; + int i; + krb5_key_data *pkey, *akey; + krb5_timestamp last_pwd_change, mod_date; + krb5_principal mod_princ; + krb5_error_code kret; + + try2read = (char *) NULL; + (*linenop)++; + retval = 1; + memset((char *)&dbent, 0, sizeof(dbent)); + + /* Make sure we've got key_data entries */ + if (krb5_dbe_create_key_data(kcontext, &dbent) || + krb5_dbe_create_key_data(kcontext, &dbent)) { + krb5_db_free_principal(kcontext, &dbent, 1); + return(1); + } + pkey = &dbent.key_data[0]; + akey = &dbent.key_data[1]; + + /* + * Match the sizes. 6 tokens to match. + */ + nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t", + &name_len, &mod_name_len, &key_len, + &alt_key_len, &salt_len, &alt_salt_len); + if (nmatched == 6) { + pkey->key_data_length[0] = key_len; + akey->key_data_length[0] = alt_key_len; + pkey->key_data_length[1] = salt_len; + akey->key_data_length[1] = alt_salt_len; + name = (char *) NULL; + mod_name = (char *) NULL; + /* + * Get the memory for the variable length fields. + */ + if ((name = (char *) malloc((size_t) (name_len + 1))) && + (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && + (!key_len || + (pkey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (key_len + 1)))) && + (!alt_key_len || + (akey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && + (!salt_len || + (pkey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (salt_len + 1)))) && + (!alt_salt_len || + (akey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) + ) { + error = 0; + + /* Read the principal name */ + if (read_string(filep, name, name_len, linenop)) { + try2read = read_name_string; + error++; + } + /* Read the key type */ + if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { + try2read = read_key_type; + error++; + } + pkey->key_data_type[0] = tmpint1; + /* Read the old format key */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[0], + pkey->key_data_length[0])) { + try2read = read_key_data; + error++; + } + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((pkey->key_data_length[0] > 4) + && (pkey->key_data_contents[0][0] == 0) + && (pkey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = pkey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = pkey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + pkey->key_data_length[0] = shortlen; + pkey->key_data_contents[0] = shortcopy; + } + + /* Read principal attributes */ + if (!error && (fscanf(filep, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", + &tmpint1, &dbent.max_life, + &dbent.max_renewable_life, + &tmpint2, &dbent.expiration, + &dbent.pw_expiration, &last_pwd_change, + &dbent.last_success, &dbent.last_failed, + &tmpint3) != 10)) { + try2read = read_pr_data1; + error++; + } + pkey->key_data_kvno = tmpint1; + dbent.fail_auth_count = tmpint3; + /* Read modifier name */ + if (!error && read_string(filep, + mod_name, + mod_name_len, + linenop)) { + try2read = read_mod_name; + error++; + } + /* Read second set of attributes */ + if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", + &mod_date, &dbent.attributes, + &tmpint1) != 3)) { + try2read = read_pr_data2; + error++; + } + pkey->key_data_type[1] = tmpint1; + /* Read salt data */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[1], + pkey->key_data_length[1])) { + try2read = read_salt_data; + error++; + } + /* Read alternate key type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_akey_type; + error++; + } + akey->key_data_type[0] = tmpint1; + /* Read alternate key */ + if (!error && read_octet_string(filep, + akey->key_data_contents[0], + akey->key_data_length[0])) { + try2read = read_akey_data; + error++; + } + + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((akey->key_data_length[0] > 4) + && (akey->key_data_contents[0][0] == 0) + && (akey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = akey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = akey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + akey->key_data_length[0] = shortlen; + akey->key_data_contents[0] = shortcopy; + } + + /* Read alternate salt type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_asalt_type; + error++; + } + akey->key_data_type[1] = tmpint1; + /* Read alternate salt data */ + if (!error && read_octet_string(filep, + akey->key_data_contents[1], + akey->key_data_length[1])) { + try2read = read_asalt_data; + error++; + } + /* Read expansion data - discard it */ + if (!error) { + for (i=0; i<8; i++) { + if (fscanf(filep, "\t%u", &tmpint1) != 1) { + try2read = read_exp_data; + error++; + break; + } + } + if (!error) + find_record_end(filep, fname, *linenop); + } + + /* + * If no error, then we're done reading. Now parse the names + * and store the database dbent. + */ + if (!error) { + if (!(kret = krb5_parse_name(kcontext, + name, + &dbent.princ))) { + if (!(kret = krb5_parse_name(kcontext, + mod_name, + &mod_princ))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(kcontext, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(kcontext, + &dbent, + last_pwd_change))) { + int one = 1; + + dbent.len = KRB5_KDB_V1_BASE_LENGTH; + pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? + 2 : 1; + akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? + 2 : 1; + if ((pkey->key_data_type[0] == + akey->key_data_type[0]) && + (pkey->key_data_type[1] == + akey->key_data_type[1])) + dbent.n_key_data--; + else if ((akey->key_data_type[0] == 0) + && (akey->key_data_length[0] == 0) + && (akey->key_data_type[1] == 0) + && (akey->key_data_length[1] == 0)) + dbent.n_key_data--; + if ((kret = krb5_db_put_principal(kcontext, + &dbent, + &one)) || + (one != 1)) { + fprintf(stderr, store_err_fmt, + fname, *linenop, name, + error_message(kret)); + error++; + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + dbent.n_key_data = 2; + } + krb5_free_principal(kcontext, mod_princ); + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, mod_name, + error_message(kret)); + error++; + } + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + error++; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + + krb5_db_free_principal(kcontext, &dbent, 1); + if (mod_name) + free(mod_name); + if (name) + free(name); + } + else { + if (nmatched != EOF) + fprintf(stderr, rhead_err_fmt, fname, *linenop); + else + retval = -1; + } + return(retval); +} + +/* + * process_k5beta6_record() - Handle a dump record in krb5b6 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta6_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int retval; + krb5_db_entry dbentry; + krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; + int nread; + int error; + int i, j, one; + char *name; + krb5_key_data *kp, *kdatap; + krb5_tl_data **tlp, *tl; + krb5_octet *op; + krb5_error_code kret; + const char *try2read; + + try2read = (char *) NULL; + memset((char *) &dbentry, 0, sizeof(dbentry)); + (*linenop)++; + retval = 1; + name = (char *) NULL; + kp = (krb5_key_data *) NULL; + op = (krb5_octet *) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); + if (nread == 5) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + /* Get memory for and form tagged data linked list */ + tlp = &dbentry.tl_data; + for (i=0; itl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(kcontext, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* Get the tagged data */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (ikey_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; jkey_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(kcontext, + &dbentry, + &one))) { + fprintf(stderr, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + } + else { + fprintf(stderr, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(kcontext, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + +int process_k5beta7_policy(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + int nread, ret; + + (*linenop)++; + rec.name = namebuf; + + nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d", rec.name, + &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &rec.policy_refcnt); + if (nread == EOF) + return -1; + else if (nread != 7) { + fprintf(stderr, "cannot parse policy on line %d (%d read)\n", + *linenop, nread); + return 1; + } + + if (ret = osa_adb_create_policy(pol_db, &rec)) { + fprintf(stderr, "cannot create policy on line %d: %s\n", + *linenop, error_message(ret)); + return 1; + } + if (verbose) + fprintf(stderr, "created policy %s\n", rec.name); + + return 0; +} + + +/* + * process_k5beta7_record() - Handle a dump record in krb5b6 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta7_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%100s\t", rectype); + if (nread == EOF) + return -1; + else if (nread != 1) + return 1; + if (strcmp(rectype, "princ") == 0) + process_k5beta6_record(fname, kcontext, filep, verbose, + linenop, pol_db); + else if (strcmp(rectype, "policy") == 0) + process_k5beta7_policy(fname, kcontext, filep, verbose, + linenop, pol_db); + else { + fprintf(stderr, "unknown record type \"%s\" on line %d\n", + rectype, *linenop); + return 1; + } + + return 0; +} + +/* + * restore_dump() - Restore the database from any version dump file. + */ +static int +restore_dump(programname, kcontext, dumpfile, f, verbose, dump, pol_db) + char *programname; + krb5_context kcontext; + char *dumpfile; + FILE *f; + int verbose; + dump_version *dump; + osa_adb_policy_t pol_db; +{ + int error; + int lineno; + + error = 0; + lineno = 1; + + /* + * Process the records. + */ + while (!(error = (*dump->load_record)(dumpfile, + kcontext, + f, + verbose, + &lineno, + pol_db))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + return(error); +} + +/* + * Usage is + * load_db [-old] [-verbose] [-update] filename dbname + */ +void +load_db(argc, argv) + int argc; + char **argv; +{ + kadm5_config_params newparams; + osa_adb_policy_t pol_db; + krb5_error_code kret; + krb5_context kcontext; + FILE *f; + extern char *optarg; + extern int optind; + char *programname; + char *dumpfile; + char *dbname, *adbname; + char *dbname_tmp, *adbname_real; + char buf[BUFSIZ]; + dump_version *load; + int update, verbose; + int aindex; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + dumpfile = (char *) NULL; + dbname = (char *) NULL; + load = NULL; + update = 0; + verbose = 0; + exit_status = 0; + dbname_tmp = (char *) NULL; + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) + load = &old_version; + else if (!strcmp(argv[aindex], b6option)) + load = &beta6_version; + else if (!strcmp(argv[aindex], verboseoption)) + verbose = 1; + else if (!strcmp(argv[aindex], updateoption)) + update = 1; + else + break; + } + if ((argc - aindex) != 2 && (argc - aindex) != 3) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + + dumpfile = argv[aindex]; + dbname = argv[aindex+1]; + adbname = argv[aindex+2]; + if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ + strlen(dump_tmptrail)+1))) { + fprintf(stderr, no_name_mem_fmt, argv[0]); + exit_status++; + return; + } + strcpy(dbname_tmp, dbname); + strcat(dbname_tmp, dump_tmptrail); + + /* + * Initialize the Kerberos context and error tables. + */ + if ((kret = krb5_init_context(&kcontext))) { + fprintf(stderr, ctx_err_fmt, programname); + free(dbname_tmp); + exit_status++; + return; + } + krb5_init_ets(kcontext); + + /* + * Open the dumpfile + */ + if (dumpfile) { + if ((f = fopen(dumpfile, "r+")) == NULL) { + fprintf(stderr, dfile_err_fmt, programname, dumpfile, + error_message(errno)); + exit_status++; + return; + } + if (kret = krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_SHARED)) { + fprintf(stderr, "%s: Cannot lock %s: %s\n", programname, + dumpfile, error_message(errno)); + exit_status++; + return; + } + } else + f = stdin; + + /* + * Auto-detect dump version if we weren't told, verify if we + * were told. + */ + fgets(buf, sizeof(buf), f); + if (load) { + if (strcmp(buf, load->header) != 0) { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + exit_status++; + if (dumpfile) fclose(f); + return; + } + } else { + /* perhaps this should be in an array, but so what? */ + if (strcmp(buf, old_version.header) == 0) + load = &old_version; + else if (strcmp(buf, beta6_version.header) == 0) + load = &beta6_version; + else if (strcmp(buf, beta7_version.header) == 0) + load = &beta7_version; + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + exit_status++; + if (dumpfile) fclose(f); + return; + } + + if (load != &beta7_version && adbname != NULL) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + } + + /* + * Cons up config params for new policy database. Use adbname is + * specified, otherwise let the policy dbname key off the dbname. + * However, after the name is retrieved, change the actual file + * name to a temp name that we'll rename later (but use the + * correct lock file). + */ + newparams = global_params; + newparams.mask &= ~(KADM5_CONFIG_ADBNAME | KADM5_CONFIG_ADB_LOCKFILE); + newparams.dbname = dbname; + newparams.mask |= KADM5_CONFIG_DBNAME; + if (adbname) { + newparams.admin_dbname = adbname; + newparams.mask |= KADM5_CONFIG_ADBNAME; + } + if (kret = kadm5_get_config_params(kcontext, NULL, NULL, &newparams, + &newparams)) { + fprintf(stderr, "%s while retrieiving configuration " + "parameters.\n", error_message(kret)); + if (dumpfile) fclose(f); + exit_status++; + return; + } + adbname_real = newparams.admin_dbname; + newparams.admin_dbname = (char *) malloc(strlen(adbname_real) + + strlen(dump_tmptrail) + 1); + strcpy(newparams.admin_dbname, adbname_real); + strcat(newparams.admin_dbname, dump_tmptrail); + + /* + * Create the new database if not an update restoration. Always + * create the policy db, even if we are not loading a dump file + * with policy info, because they may be loading an old dump + * intending to use it with the new kadm5 system (ie: using load + * as create). + */ + if (!update && (kret = krb5_db_create(kcontext, dbname_tmp))) { + fprintf(stderr, dbcreaterr_fmt, + programname, dbname, error_message(kret)); + exit_status++; + kadm5_free_config_params(kcontext, &newparams); + if (dumpfile) fclose(f); + return; + } + if (!update && (kret = osa_adb_create_policy_db(&newparams))) { + fprintf(stderr, "%s: %s while creating policy database\n", + programname, error_message(kret)); + kadm5_free_config_params(kcontext, &newparams); + if (dumpfile) fclose(f); + return; + } + + /* + * Point ourselves at the new databases. + */ + if (kret = krb5_db_set_name(kcontext, + (update) ? dbname : dbname_tmp)) { + fprintf(stderr, dbname_err_fmt, + programname, + (update) ? dbname : dbname_tmp, error_message(kret)); + exit_status++; + goto error; + } + if (kret = osa_adb_open_policy(&pol_db, &newparams)) { + fprintf(stderr, "%s: %s while opening policy database\n", + programname, error_message(kret)); + exit_status++; + goto error; + } + + /* + * Initialize the database. + */ + if (kret = krb5_db_init(kcontext)) { + fprintf(stderr, dbinit_err_fmt, + programname, error_message(kret)); + exit_status++; + goto error; + } + + if (restore_dump(programname, kcontext, (dumpfile) ? dumpfile : stdin_name, + f, verbose, load, pol_db)) { + fprintf(stderr, restfail_fmt, + programname, load->name); + exit_status++; + } + + if ((kret = krb5_db_fini(kcontext)) || + (kret = osa_adb_close_policy(pol_db))) { + fprintf(stderr, close_err_fmt, + programname, error_message(kret)); + exit_status++; + } + +error: + /* + * If there was an error and this is not an update, then + * destroy the database. + */ + if (!update) { + if (exit_status) { + if ((kret = kdb5_db_destroy(kcontext, dbname))) { + fprintf(stderr, dbdelerr_fmt, + programname, dbname_tmp, error_message(kret)); + exit_status++; + } + } + else { + if ((kret = krb5_db_rename(kcontext, + dbname_tmp, + dbname))) { + fprintf(stderr, dbrenerr_fmt, + programname, dbname_tmp, dbname, + error_message(kret)); + exit_status++; + } + + if (kret = osa_adb_get_lock(pol_db, OSA_ADB_PERMANENT)) { + fprintf(stderr, + "%s: %s while getting permanent lock\n", + programname, error_message(kret)); + exit_status++; + } else if (rename(newparams.admin_dbname, + adbname_real) < 0) { + fprintf(stderr, "%s: %s while renaming %s to %s\n", + programname, error_message(kret), + newparams.admin_dbname, adbname_real); + exit_status++; + } else if (kret = osa_adb_release_lock(pol_db)) { + fprintf(stderr, "%s: %s while unlocking %s\n", + programname, error_message(kret), + adbname_real); + exit_status++; + } + } + } + + if (dumpfile) { + (void) krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_UNLOCK); + fclose(f); + } + + free(newparams.admin_dbname); + newparams.admin_dbname = adbname_real; + kadm5_free_config_params(kcontext, &newparams); + free(dbname_tmp); + krb5_free_context(kcontext); +} diff --git a/src/kadmin/dbutil/dumpv4.c b/src/kadmin/dbutil/dumpv4.c new file mode 100644 index 000000000..a51626db3 --- /dev/null +++ b/src/kadmin/dbutil/dumpv4.c @@ -0,0 +1,411 @@ +/* + * admin/edit/dumpv4.c + * + * Copyright 1990,1991, 1994 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. + * + * + * Dump a KDC database into a V4 slave dump. + */ + +#ifdef KRB5_KRB4_COMPAT + +#include "k5-int.h" +#include "com_err.h" + +#include +#include +#include +/* MKEYFILE is now defined in kdc.h */ +#include + +#include +#include "kdb5_util.h" + +struct dump_record { + char *comerr_name; + FILE *f; + krb5_encrypt_block *v5master; + C_Block v4_master_key; + Key_schedule v4_master_key_schedule; + long master_key_version; + char *realm; +}; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; + +void update_ok_file(); + +#define ANAME_SZ 40 +#define INST_SZ 40 + +static char *v4_mkeyfile = "/.k"; + +static int +v4init(arg, manual) + struct dump_record *arg; + int manual; +{ + int fd; + int ok = 0; + + if (!manual) { + fd = open(v4_mkeyfile, O_RDONLY, 0600); + if (fd >= 0) { + if (read(fd,arg->v4_master_key,sizeof(C_Block)) == sizeof(C_Block)) + ok = 1; + close(fd); + } + } + if (!ok) { + des_read_password(arg->v4_master_key, "V4 Kerberos master key: ", 1); + printf("\n"); + } + arg->master_key_version = 1; + key_sched(arg->v4_master_key, arg->v4_master_key_schedule); + + return 0; +} + +v4_print_time(file, timeval) + FILE *file; + unsigned long timeval; +{ + struct tm *tm; + struct tm *gmtime(); + tm = gmtime((time_t *)&timeval); + fprintf(file, " %04d%02d%02d%02d%02d", + tm->tm_year < 1900 ? tm->tm_year + 1900: tm->tm_year, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min); +} + + + +krb5_error_code +dump_v4_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + struct dump_record *arg = (struct dump_record *) ptr; + krb5_principal mod_princ; + krb5_timestamp mod_time; + krb5_error_code retval; + int i, max_kvno, ok_key; + + struct v4princ { + char name[ANAME_SZ+1]; + char instance[INST_SZ+1]; + char realm[REALM_SZ+1]; + int max_life; + int kdc_key_ver, key_version, attributes; + char mod_name[ANAME_SZ+1]; + char mod_instance[INST_SZ+1]; + char mod_realm[REALM_SZ+1]; + } v4princ, *principal; + des_cblock v4key; + + principal = &v4princ; + + if (strcmp(krb5_princ_realm(util_context, entry->princ)->data, arg->realm)) + /* skip this because it's a key for a different realm, probably + * a paired krbtgt key */ + return 0; + + retval = krb5_524_conv_principal(util_context, entry->princ, + principal->name, principal->instance, + principal->realm); + if (retval) + /* Skip invalid V4 principals */ + return 0; + + if (!strcmp(principal->name, "K") && !strcmp(principal->instance, "M")) + /* The V4 master key is handled specially */ + return 0; + + if (! principal->name[0]) + return 0; + if (! principal->instance[0]) + strcpy(principal->instance, "*"); + + /* Now move to mod princ */ + if (retval = krb5_dbe_lookup_mod_princ_data(util_context,entry, + &mod_time, &mod_princ)){ + com_err(arg->comerr_name, retval, "while unparsing db entry"); + exit_status++; + return retval; + } + retval = krb5_524_conv_principal(util_context, mod_princ, + principal->mod_name, principal->mod_instance, + principal->mod_realm); + if (retval) { + /* Invalid V4 mod principal */ + principal->mod_name[0] = '\0'; + principal->mod_instance[0] = '\0'; + } + + if (! principal->mod_name[0]) + strcpy(principal->mod_name, "*"); + if (! principal->mod_instance[0]) + strcpy(principal->mod_instance, "*"); + + /* OK deal with the key now. */ + for (max_kvno = i = 0; i < entry->n_key_data; i++) { + if (max_kvno < entry->key_data[i].key_data_kvno) { + max_kvno = entry->key_data[i].key_data_kvno; + ok_key = i; + } + } + + i = ok_key; + while (ok_key < entry->n_key_data) { + if (max_kvno == entry->key_data[ok_key].key_data_kvno) { + if (entry->key_data[ok_key].key_data_type[1] + == KRB5_KDB_SALTTYPE_V4) { + goto found_one; + } + } + ok_key++; + } + + /* See if there are any DES keys that may be suitable */ + ok_key = i; + while (ok_key < entry->n_key_data) { + if (max_kvno == entry->key_data[ok_key].key_data_kvno) { + krb5_enctype enctype = entry->key_data[ok_key].key_data_type[0]; + if ((enctype == ENCTYPE_DES_CBC_CRC) || + (enctype == ENCTYPE_DES_CBC_MD5) || + (enctype == ENCTYPE_DES_CBC_RAW)) + goto found_one; + } + ok_key++; + } + /* skip this because it's a new style key and we can't help it */ + return 0; + +found_one:; + principal->key_version = max_kvno; + if ((principal->max_life = entry->max_life / (60 * 5)) > 255) + principal->max_life = 255; + principal->kdc_key_ver = arg->master_key_version; + principal->attributes = 0; /* ??? not preserved either */ + + fprintf(arg->f, "%s %s %d %d %d %d ", + principal->name, + principal->instance, + principal->max_life, + principal->kdc_key_ver, + principal->key_version, + principal->attributes); + + handle_one_key(arg, arg->v5master, &entry->key_data[ok_key], v4key); + + for (i = 0; i < 8; i++) { + fprintf(arg->f, "%02x", ((unsigned char*)v4key)[i]); + if (i == 3) fputc(' ', arg->f); + } + + v4_print_time(arg->f, entry->expiration); + v4_print_time(arg->f, mod_time); + + fprintf(arg->f, " %s %s\n", principal->mod_name, principal->mod_instance); + return 0; +} + +/*ARGSUSED*/ +void dump_v4db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_record arg; + + if (argc > 2) { + com_err(argv[0], 0, "Usage: %s filename", argv[0]); + exit_status++; + return; + } + if (!dbactive) { + com_err(argv[0], 0, Err_no_database); + exit_status++; + return; + } + if (argc == 2) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependancies than I + * want to get into. + */ + unlink(argv[1]); + if (!(f = fopen(argv[1], "w"))) { + com_err(argv[0], errno, + "While opening file %s for writing", argv[1]); + exit_status++; + return; + } + } else { + f = stdout; + } + + arg.comerr_name = argv[0]; + arg.f = f; + v4init(&arg, 0); + handle_keys(&arg); + + /* special handling for K.M since it isn't preserved */ + { + des_cblock v4key; + int i; + + /* assume: + max lifetime (255) + key version == 1 (actually, should be whatever the v5 one is) + master key version == key version + args == 0 (none are preserved) + expiration date is the default 2000 + last mod time is near zero (arbitrarily.) + creator is db_creation * + */ + + fprintf(f,"K M 255 1 1 0 "); + +#ifndef KDB4_DISABLE + kdb_encrypt_key (arg.v4_master_key, v4key, + arg.v4_master_key, arg.v4_master_key_schedule, + ENCRYPT); +#else /* KDB4_DISABLE */ + pcbc_encrypt((C_Block *) arg.v4_master_key, + (C_Block *) v4key, + (long) sizeof(C_Block), + arg.v4_master_key_schedule, + (C_Block *) arg.v4_master_key, + ENCRYPT); +#endif /* KDB4_DISABLE */ + + for (i=0; i<8; i++) { + fprintf(f, "%02x", ((unsigned char*)v4key)[i]); + if (i == 3) fputc(' ', f); + } + fprintf(f," 200001010459 197001020000 db_creation *\n"); + } + + (void) krb5_db_iterate(util_context, dump_v4_iterator, + (krb5_pointer) &arg); + if (argc == 2) + fclose(f); + if (argv[1]) + update_ok_file(argv[1]); +} + +int handle_keys(arg) + struct dump_record *arg; +{ + krb5_error_code retval; + char *defrealm; + char *mkey_name = 0; + char *mkey_fullname; + krb5_principal master_princ; + + if (retval = krb5_get_default_realm(util_context, &defrealm)) { + com_err(arg->comerr_name, retval, + "while retrieving default realm name"); + exit(1); + } + arg->realm = defrealm; + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(util_context, mkey_name, arg->realm, + &mkey_fullname, &master_princ)) { + com_err(arg->comerr_name, retval, "while setting up master key name"); + exit(1); + } + + krb5_use_enctype(util_context, &master_encblock, DEFAULT_KDC_ENCTYPE); + if (retval = krb5_db_fetch_mkey(util_context, master_princ, + &master_encblock, 0, + 0, (char *) NULL, 0, &master_keyblock)) { + com_err(arg->comerr_name, retval, "while reading master key"); + exit(1); + } + if (retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock)) { + com_err(arg->comerr_name, retval, "while processing master key"); + exit(1); + } + arg->v5master = &master_encblock; + return(0); +} + +handle_one_key(arg, v5master, v5key, v4key) + struct dump_record *arg; + krb5_encrypt_block *v5master; + krb5_key_data *v5key; + des_cblock v4key; +{ + krb5_error_code retval; + + krb5_keyblock v4v5key; + krb5_keyblock v5plainkey; + /* v4key is the actual v4 key from the file. */ + + if (retval = krb5_dbekd_decrypt_key_data(util_context, v5master, v5key, + &v5plainkey, NULL)) + return retval; + + /* v4v5key.contents = (krb5_octet *)v4key; */ + /* v4v5key.enctype = ENCTYPE_DES; */ + /* v4v5key.length = sizeof(v4key); */ + + memcpy(v4key, v5plainkey.contents, sizeof(des_cblock)); +#ifndef KDB4_DISABLE + kdb_encrypt_key (v4key, v4key, + arg->v4_master_key, arg->v4_master_key_schedule, + ENCRYPT); +#else /* KDB4_DISABLE */ + pcbc_encrypt((C_Block *) v4key, + (C_Block *) v4key, + (long) sizeof(C_Block), + arg->v4_master_key_schedule, + (C_Block *) arg->v4_master_key, + ENCRYPT); +#endif /* KDB4_DISABLE */ + return 0; +} + +#else /* KRB5_KRB4_COMPAT */ +void dump_v4db(argc, argv) + int argc; + char **argv; +{ + printf("This version of krb5_edit does not support the V4 dump command.\n"); +} +#endif /* KRB5_KRB4_COMPAT */ diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c new file mode 100644 index 000000000..d31ce3319 --- /dev/null +++ b/src/kadmin/dbutil/kadm5_create.c @@ -0,0 +1,241 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include +#include +#include +#include +#include + +#include +#include + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +extern char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: kadm5_create + * + * Purpose: create admin principals in KDC database + * + * Arguments: params (r) configuration parameters to use + * + * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE + * principals in the KDC database and sets their attributes + * appropriately. + */ +int kadm5_create(kadm5_config_params *params) +{ + int retval; + void *handle; + krb5_context context; + FILE *f; + + + if (retval = krb5_init_context(&context)) + exit(ERR); + + /* + * The lock file has to exist before calling kadm5_init, but + * params->admin_lockfile may not be set yet... + */ + if (retval = kadm5_get_config_params(context, NULL, NULL, + params, params)) { + com_err(whoami, retval, str_INITING_KCONTEXT); + return 1; + } + + if (retval = osa_adb_create_policy_db(params)) { + com_err(whoami, retval, str_CREATING_POLICY_DB); + return 1; + } + + if ((retval = kadm5_init(whoami, NULL, NULL, params, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, params->realm); + + kadm5_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + return 0; +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates KADM5_ADMIN_SERVICE, + * KADM5_CHANGEPW_SERVICE. If any of these exist a message is + * printed. If any of these existing principal do not have the proper + * attributes, a warning message is printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + KADM5_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + kadm5_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = kadm5_create_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES), + "to-be-random")) { + if (ret == KADM5_DUP) + ret = kadm5_modify_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c new file mode 100644 index 000000000..b7520d98d --- /dev/null +++ b/src/kadmin/dbutil/kdb5_create.c @@ -0,0 +1,449 @@ +/* + * admin/create/kdb5_create.c + * + * Copyright 1990,1991 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. + * + * + * Generate (from scratch) a Kerberos KDC database. + */ + +#include +#include +#include +#include + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + TGT_KEY /* special handling for tgt key */ +}; + +krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; + krb5_int32 nkslist; + krb5_key_salt_tuple *kslist; +} rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + (krb5_encrypt_block *) NULL, + (krb5_pointer) NULL, + 1, + &def_kslist +}; + +struct iterate_args { + krb5_context ctx; + struct realm_info *rblock; + krb5_db_entry *dbentp; +}; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +/* + * Steps in creating a database: + * + * 1) use the db calls to open/create a new database + * + * 2) get a realm name for the new db + * + * 3) get a master password for the new db; convert to an encryption key. + * + * 4) create various required entries in the database + * + * 5) close & exit + */ + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_encrypt_block master_encblock; +krb5_data master_salt; + +krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +static char *mkey_password = 0; +char *whoami; + +extern int exit_status; +extern osa_adb_policy_t policy_db; +extern kadm5_config_params global_params; +extern krb5_context util_context; + +static void usage() +{ + fprintf(stderr, "usage: %s [-s]\n", whoami); + exit_status++; +} + +void kdb5_create(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int optchar; + + krb5_error_code retval; + char *mkey_fullname; + char *pw_str = 0; + int pw_size = 0; + int do_stash = 0; + krb5_data pwd; + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + whoami = argv[0]; + + mkey_password = NULL; + optind = 1; + while ((optchar = getopt(argc, argv, "P:s")) != EOF) { + switch(optchar) { + case 's': + do_stash++; + break; + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + break; + case '?': + default: + usage(); + return; + } + } + + rblock.max_life = global_params.max_life; + rblock.max_rlife = global_params.max_rlife; + rblock.expiration = global_params.expiration; + rblock.flags = global_params.flags; + rblock.nkslist = global_params.num_keysalts; + rblock.kslist = global_params.keysalts; + + krb5_use_enctype(util_context, &master_encblock, master_keyblock.enctype); + + retval = krb5_db_set_name(util_context, global_params.dbname); + if (!retval) retval = EEXIST; + + if (retval == EEXIST || retval == EACCES || retval == EPERM) { + /* it exists ! */ + com_err(argv[0], 0, "The database '%s' appears to already exist", + global_params.dbname); + exit_status++; return; + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + &mkey_fullname, &master_princ))) { + com_err(argv[0], retval, "while setting up master key name"); + exit_status++; return; + } + + krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); + krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); + krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; + krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + global_params.dbname, global_params.realm, mkey_fullname); + + if (!mkey_password) { + printf("You will be prompted for the database Master Password.\n"); + printf("It is important that you NOT FORGET this password.\n"); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + + retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(argv[0], retval, "while reading master key from keyboard"); + exit_status++; return; + } + mkey_password = pw_str; + } + + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &master_salt); + if (retval) { + com_err(argv[0], retval, "while calculated master key salt"); + exit_status++; return; + } + if (retval = krb5_string_to_key(util_context, &master_encblock, + &master_keyblock, &pwd, &master_salt)) { + com_err(argv[0], retval, "while transforming master key from password"); + exit_status++; return; + } + + if ((retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock))) { + com_err(argv[0], retval, "while processing master key"); + exit_status++; return; + } + + rblock.eblock = &master_encblock; + if ((retval = krb5_init_random_key(util_context, &master_encblock, + &master_keyblock, &rblock.rseed))) { + com_err(argv[0], retval, "while initializing random key generator"); + (void) krb5_finish_key(util_context, &master_encblock); + exit_status++; return; + } + if ((retval = krb5_db_create(util_context, global_params.dbname))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while creating database '%s'", + global_params.dbname); + exit_status++; return; + } + if (retval = krb5_db_fini(util_context)) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &rblock.rseed); + com_err(argv[0], retval, "while closing current database"); + exit_status++; return; + } + if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while setting active database to '%s'", + global_params.dbname); + exit_status++; return; + } + if ((retval = krb5_db_init(util_context))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while initializing the database '%s'", + global_params.dbname); + exit_status++; return; + } + + if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) || + (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) { + (void) krb5_db_fini(util_context); + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while adding entries to the database"); + exit_status++; return; + } + /* + * Always stash the master key so kadm5_create does not prompt for + * it; delete the file below if it was not requested. DO NOT EXIT + * BEFORE DELETING THE KEYFILE if do_stash is not set. + */ + if (retval = krb5_db_store_mkey(util_context, + global_params.stash_file, + master_princ, + &master_keyblock)) { + com_err(argv[0], errno, "while storing key"); + printf("Warning: couldn't stash master key.\n"); + } + /* clean up */ + (void) krb5_db_fini(util_context); + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + free(master_keyblock.contents); + if (pw_str) { + memset(pw_str, 0, pw_size); + free(pw_str); + } + free(master_salt.data); + + if (kadm5_create(&global_params)) { + if (!do_stash) unlink(global_params.stash_file); + exit_status++; + return; + } + if (!do_stash) unlink(global_params.stash_file); + + /* now open the database */ + open_db_and_mkey(); + + return; + +} + +static krb5_error_code +tgt_keysalt_iterate(ksent, ptr) + krb5_key_salt_tuple *ksent; + krb5_pointer ptr; +{ + krb5_context context; + krb5_error_code kret; + struct iterate_args *iargs; + krb5_keyblock random_keyblock, *key; + krb5_int32 ind; + krb5_encrypt_block random_encblock; + krb5_pointer rseed; + krb5_data pwd; + + iargs = (struct iterate_args *) ptr; + kret = 0; + + context = iargs->ctx; + + /* + * Convert the master key password into a key for this particular + * encryption system. + */ + krb5_use_enctype(context, &random_encblock, ksent->ks_enctype); + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock, + &pwd, &master_salt)) + return kret; + if ((kret = krb5_init_random_key(context, &random_encblock, + &random_keyblock, &rseed))) + return kret; + + if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { + ind = iargs->dbentp->n_key_data-1; + if (!(kret = krb5_random_key(context, + &random_encblock, rseed, + &key))) { + kret = krb5_dbekd_encrypt_key_data(context, + iargs->rblock->eblock, + key, + NULL, + 1, + &iargs->dbentp->key_data[ind]); + krb5_free_keyblock(context, key); + } + } + memset((char *)random_keyblock.contents, 0, random_keyblock.length); + free(random_keyblock.contents); + (void) krb5_finish_random_key(context, &random_encblock, &rseed); + return(kret); +} + +static krb5_error_code +add_principal(context, princ, op, pblock) + krb5_context context; + krb5_principal princ; + enum ap_op op; + struct realm_info *pblock; +{ + krb5_error_code retval; + krb5_db_entry entry; + + krb5_timestamp now; + struct iterate_args iargs; + + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.attributes = pblock->flags; + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.expiration = pblock->expiration; + + if ((retval = krb5_copy_principal(context, princ, &entry.princ))) + goto error_out; + + if ((retval = krb5_timeofday(context, &now))) + goto error_out; + + if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, + now, &db_create_princ))) + goto error_out; + + switch (op) { + case MASTER_KEY: + if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) + == NULL) + goto error_out; + memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); + entry.n_key_data = 1; + + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, NULL, + 1, entry.key_data))) + return retval; + break; + case TGT_KEY: + iargs.ctx = context; + iargs.rblock = pblock; + iargs.dbentp = &entry; + /* + * Iterate through the key/salt list, ignoring salt types. + */ + if ((retval = krb5_keysalt_iterate(pblock->kslist, + pblock->nkslist, + 1, + tgt_keysalt_iterate, + (krb5_pointer) &iargs))) + return retval; + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_db_put_principal(context, &entry, &nentries); + +error_out:; + krb5_dbe_free_contents(context, &entry); + return retval; +} diff --git a/src/kadmin/dbutil/kdb5_destroy.c b/src/kadmin/dbutil/kdb5_destroy.c new file mode 100644 index 000000000..7c6873df7 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_destroy.c @@ -0,0 +1,117 @@ +/* + * admin/destroy/kdb5_destroy.c + * + * Copyright 1990 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. + * + * + * kdb_dest(roy): destroy the named database. + * + * This version knows about DBM format databases. + */ + +#include "k5-int.h" +#include +#include "com_err.h" +#include +#include + +extern int errno; +extern int exit_status; +extern krb5_boolean dbactive; +extern kadm5_config_params global_params; + +char *yes = "yes\n"; /* \n to compare against result of + fgets */ + +static void +usage(who, status) + char *who; + int status; +{ + fprintf(stderr, "usage: %s [-f]\n", who); +} + +void +kdb5_destroy(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int optchar; + char *dbname; + char buf[5]; + char dbfilename[MAXPATHLEN]; + krb5_error_code retval, retval1, retval2; + krb5_context context; + int force = 0; + + krb5_init_context(&context); + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + dbname = global_params.dbname; + + optind = 1; + while ((optchar = getopt(argc, argv, "f")) != EOF) { + switch(optchar) { + case 'f': + force++; + break; + case '?': + default: + usage(argv[0], 1); + return; + /*NOTREACHED*/ + } + } + if (!force) { + printf("Deleting KDC database stored in '%s', are you sure?\n", dbname); + printf("(type 'yes' to confirm)? "); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + exit_status++; return; + } + if (strcmp(buf, yes)) { + exit_status++; return; + } + printf("OK, deleting database '%s'...\n", dbname); + } + + if (retval = krb5_db_set_name(context, dbname)) { + com_err(argv[0], retval, "'%s'",dbname); + exit_status++; return; + } + retval1 = kdb5_db_destroy(context, dbname); + retval2 = osa_adb_destroy_policy_db(&global_params); + if (retval1) { + com_err(argv[0], retval1, "deleting database '%s'",dbname); + exit_status++; return; + } + if (retval2) { + com_err(argv[0], retval2, "destroying policy database"); + exit_status++; return; + } + + dbactive = FALSE; + printf("** Database '%s' destroyed.\n", dbname); + return; +} diff --git a/src/kadmin/dbutil/kdb5_edit.M b/src/kadmin/dbutil/kdb5_edit.M new file mode 100644 index 000000000..8405c01cd --- /dev/null +++ b/src/kadmin/dbutil/kdb5_edit.M @@ -0,0 +1,179 @@ +.\" admin/edit/kdb5_edit.M +.\" +.\" Copyright 1990 by the Massachusetts Institute of Technology. +.\" +.\" 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. +.\" +.\" +.TH KDB5_EDIT 8 "Kerberos Version 5.0" "MIT Project Athena" +.SH NAME +kdb5_edit \- edit a Kerberos V5 principal database +.SH SYNOPSIS +.B kdb5_edit +[ +.B \-r +.I realm +] [ +.B \-d +.I dbname +] [ +.B \-k +.I keytype +] [ +.B \-M +.I mkeyname +] [ +.B \-e +.I enctype +] [ +.B \-m +] [ +.B \-R +.I command +] [ +.B \-s +.I script +] [ +.B \-f +.I stashfile +] +.br +.SH DESCRIPTION +.I kdb5_edit +allows an administrator to add, delete, and edit entries in a Kerberos +version 5 principal database. +After themaster key is verified, commands are to +.I kdb5_edit +are issued using one of three mechanisms. If a single command is supplied +using the +.B \-R +.I command +argument, then that single command is processed and execution ceases. If a +script file is provided using the +.B \-s +.I script +argument, then commands are read from this file until either an error occurs +or an end of file is detected. Finally, if neither a command or a script is +specified, the invoker is placed into a shell-like command loop, from which +[s]he may issue commands to modify the +database. +.PP +The +.B \-r +.I realm +option specifies the realm of the database; +by default the realm returned by +.IR krb5_default_local_realm (3) +is used. +.PP +The +.B \-d +.I dbname +option specifies the name under which the principal database is stored; +by default the database is in DEFAULT_DBM_FILE (defined in ). +.PP +The +.B \-k +.I keytype +option specifies the key type of the master key in the database; the default is +the string representation of DEFAULT_KDC_KEYTYPE (defined in ). +.PP +The +.B \-f +.I stashfile +option specifies the filename of the stashed V5 master key. The default is +defined as DEFAULT_KEYFILE_STUB in and is +typically $(prefix)/lib/krb5kdc/.k5.REALMNAME. (In previous +releases, this would have been /.k5.REALMNAME.) +.PP +The +.B \-M +.I mkeyname +option specifies the principal name for the master key in the database; +the default is KRB5_KDB_M_NAME (defined in ). +.PP +The +.B \-e +.I enctype +option specifies the encryption type to be used when placing entries in +the database; the default is the string representation of DEFAULT_KDC_ETYPE +(defined in ). +.PP +The +.B \-m +option specifies that the master database password should be fetched +from the keyboard rather than from a file on disk. +.SH AVAILABLE COMMANDS + +The following is a list of commands and their aliases that the system +administrator may use to manipulate the database: + +.IP add_new_key,ank +Add new entry to Kerberos database (prompting for password) + +.IP change_pwd_key,cpw +Change key of an entry in the Kerberos database (prompting for password) + +.IP add_rnd_key,ark +Add new entry to Kerberos database, using a random key + +.IP change_rnd_key,crk +Change key of an entry in the Kerberos database (select a new random key) + +.IP delete_entry,delent,del +Delete an entry from the database + +.IP extract_srvtab,xst,ex_st +Extract service key table + +.IP extract_v4_srvtab,xst4 +Extract service key table + +.IP modify_entry,modent +Modify entry + +.IP list_db,ldb +List database entries + +.IP dump_db,ddb +Dump database entries to a file + +.IP load_db,lddb +Load database entries from a file + +.IP set_dbname,sdbn +Change database name + +.IP enter_master_key,emk +Enter the master key for a database + +.IP change_working_directory,cwd,cd +Change working directory + +.IP print_working_direcotry,pwd +Print working directory + +.IP list_requests,lr,? +List available requests. + +.IP quit,exit,q +Exit program. + +.SH SEE ALSO +krb5(3), krb5kdc(8), ss(3) +.SH BUGS + diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c new file mode 100644 index 000000000..6952db7ec --- /dev/null +++ b/src/kadmin/dbutil/kdb5_stash.c @@ -0,0 +1,153 @@ +/* + * admin/stash/kdb5_stash.c + * + * Copyright 1990 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. + * + * + * Store the master database key in a file. + */ + +#include "k5-int.h" +#include "com_err.h" +#include +#include + +extern int errno; + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_encrypt_block master_encblock; +extern kadm5_config_params global_params; + +extern int exit_status; + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-f keyfile]\n", who); + exit_status++; return; +} + + +void +kdb5_stash(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; + int optchar; + krb5_error_code retval; + char *dbname = (char *) NULL; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *keyfile = 0; + krb5_context context; + krb5_realm_params *rparams; + + int enctypedone = 0; + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + krb5_init_context(&context); + krb5_init_ets(context); + + dbname = global_params.dbname; + realm = global_params.realm; + mkey_name = global_params.mkey_name; + keyfile = global_params.stash_file; + + optind = 1; + while ((optchar = getopt(argc, argv, "f:")) != EOF) { + switch(optchar) { + case 'f': + keyfile = optarg; + break; + case '?': + default: + usage(argv[0], 1); + return; + } + } + + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit_status++; return; + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + if (retval = krb5_db_set_name(context, dbname)) { + com_err(argv[0], retval, "while setting active database to '%s'", + dbname); + exit_status++; return; + } + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ)) { + com_err(argv[0], retval, "while setting up master key name"); + exit_status++; return; + } + + if (retval = krb5_db_init(context)) { + com_err(argv[0], retval, "while initializing the database '%s'", + dbname); + exit_status++; return; + } + + /* TRUE here means read the keyboard, but only once */ + if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock, + TRUE, FALSE, (char *) NULL, + 0, &master_keyblock)) { + com_err(argv[0], retval, "while reading master key"); + (void) krb5_db_fini(context); + exit_status++; return; + } + if (retval = krb5_db_verify_master_key(context, master_princ, + &master_keyblock,&master_encblock)) { + com_err(argv[0], retval, "while verifying master key"); + (void) krb5_db_fini(context); + exit_status++; return; + } + if (retval = krb5_db_store_mkey(context, keyfile, master_princ, + &master_keyblock)) { + com_err(argv[0], errno, "while storing key"); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + (void) krb5_db_fini(context); + exit_status++; return; + } + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + if (retval = krb5_db_fini(context)) { + com_err(argv[0], retval, "closing database '%s'", dbname); + exit_status++; return; + } + + return; +} diff --git a/src/kadmin/dbutil/kdb5_util.M b/src/kadmin/dbutil/kdb5_util.M new file mode 100644 index 000000000..746e018c0 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.M @@ -0,0 +1,122 @@ +KDB5_UTIL(8) + +NAME + kdb5_util - Kerberos database maintainance utility + +SYNOPSIS + kdb5_util [-d dbpathname ] [-r realmname] [-R request ] + [-s scriptfile] [-k enctype] [-M mkeyname] + [-f stashfile] + +DESCRIPTION + kdb5_util allows an administrator to perform low-level + maintainance procedures on the Kerberos and KADM5 database. + Databases can be created, destroyed, and dumped to and loaded + from ASCII files. Additionally, kdb5_util can create a + Kerberos master key stash file. kdb5_util subsumes the + functionality of and makes obsolete the previous database + maintainance programs kdb5_create, kdb5_edit, kdb5_destroy, + and kdb5_stash. + + When the program is first run, it attempts to acquire the + master key and open the database. Execution continues whether + or not it is successful, however, because the database may not + exist yet or the stash file may be corrupt. Commands can be + issued using one of three mechanisms. If a single command is + supplied using the request argument, then that single command + is processed and execution ceases. If a script file is + provided using the -s script argument, then commands are read + from this file until either an error occurs or an end of file + is detected. Finally, if neither a command or a script is + specified, the invoker is placed into a shell-like command + loop, from which commands may be executed. + + The -r realm option specifies the realm of the database; by + default the realm returned by krb5_default_local_realm(3) is + used. + + The -d dbname option specifies the name under which the + principal database is stored; by default the database is + controlled by kdc.conf. The KADM5 policy database and lock + file are also derived from this value. + + The -k keytype option specifies the key type of the master key + in the database; the default is controlled by kdc.conf. + + The -f stashfile option specifies the filename of the stashed + V5 master key. The default is controlled by kdc.conf and is + typically /lib/krb5kdc/.k5.REALMNAME. (In + previous releases, this would have been /.k5.REALMNAME.) + + The -M mkeyname option specifies the principal name for the + master key in the database; the default is controlled by + kdc.conf. + + The -m option specifies that the master database password + should be fetched from the keyboard rather than from a file on + disk. + +AVAILABLE COMMANDS + create_db [-s] + + Alias: create. Creates a new database. If the -s option is + specified, the stash file is also created. This command fails + if the database already exists. If the command is successful, + the database is opened just as if it had already existed when + the program was first run. + + destroy_db [-f] + + Alias: destroy. Destroys the database, first overwriting the + disk sectors and then unlinking the files, after prompting the + user for confirmation. With the -f argument, does not prompt + the user. + + stash_mkey [-f keyfile] + + Alias: stash. Stores the master principal's keys in a stash + file. The -f argument can be used to override the keyfile + specified at startup. + + dump_db [-old] [-b6] [-verbose] [filename [principals...]] + + Alias: ddb. Dumps the current Kerberos and KADM5 database + into an ASCII file. By default, the database is dumped in + current format, "kdb5_util load_dump version 4". The -b6 + argument causes the dump to be in the Kerberos 5 Beta 6 format + ("kdb5_edit load_dump version 3.0"). The -old argument causes + the dump to be in the Kerberos 5 Beta 5 and earlier dump + format ("kdb5_edit load_dump version 2.0"). The -verbose + option causes the name of each principal and policy to be + printed as it is dumped. + + load_db [-old] [-b6] [-verbose] [-update] filename dbname + [admin_dbname] + + Alias: lddb. Loads a database dump from the named file into + the named database. The -old and -b6 options require the dump + to be in the specified format (see dump_db); otherwise, the + format of the dump file is detected automatically and handled + as appropriate. If the -update argument is specified, records + from the dump file are merely added to or updated in the + existing database; otherwise, a new database is created + containing only what is in the dump file and the old one + destroyed on a successful completion. The dbname argument is + required (XXX probably shouldn't be) and overrides the value + specified on the command line or the default. The + admin_dbname is optional and is derived from dbname if not + specified. + + dump_v4db [filename] + + Alias: d4db. Dumps the current database into the Kerberos 4 + database dump format. + + load_v4db [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] + [-k enctype] [-M mkeyname] -f inputfile + + Alias: lddb4. Loads a Kerberos 4 database dump file. XXX Not + sure what all the arguments mean. + +SEE ALSO + kadm5_export(8), kadm5_import(8) diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c new file mode 100644 index 000000000..3f31fcb14 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.c @@ -0,0 +1,416 @@ +/* + * admin/edit/kdb5_edit.c + * + * (C) Copyright 1990,1991, 1996 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. + * + * + * Edit a KDC database. + */ + +#include +#include +#include +#include +#include +#include "kdb5_util.h" + +char *Err_no_master_msg = "Master key not entered!\n"; +char *Err_no_database = "Database not currently opened!\n"; + +/* + * XXX Ick, ick, ick. These global variables shouldn't be global.... + */ +static char *mkey_password = 0; + +/* + * I can't figure out any way for this not to be global, given how ss + * works. + */ + +int exit_status = 0; +krb5_context util_context; +osa_adb_policy_t policy_db; +kadm5_config_params global_params; + +/* + * Script input, specified by -s. + */ +FILE *scriptfile = (FILE *) NULL; + +static void +usage(who, status) + char *who; + int status; +{ + fprintf(stderr, + "usage: %s [-d dbpathname ] [-r realmname] [-R request ]\n", + who); + fprintf(stderr, "\t [-k enctype] [-M mkeyname] [-f stashfile]\n"); + exit(status); +} + +krb5_keyblock master_keyblock; +krb5_principal master_princ; +krb5_db_entry master_entry; +krb5_encrypt_block master_encblock; +krb5_pointer master_random; +int valid_master_key = 0; + +char *progname; +krb5_boolean manual_mkey = FALSE; +krb5_boolean dbactive = FALSE; + +char *kdb5_util_Init(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + int optchar; + krb5_error_code retval; + char *request = NULL; + + retval = krb5_init_context(&util_context); + if (retval) { + fprintf(stderr, "krb5_init_context failed with error #%ld\n", + (long) retval); + exit(1); + } + krb5_init_ets(util_context); + initialize_adb_error_table(); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + progname = argv[0]; + + while ((optchar = getopt(argc, argv, "P:d:a:r:R:k:M:e:ms:f:")) != EOF) { + switch(optchar) { + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + manual_mkey = TRUE; + break; + case 'd': + global_params.dbname = optarg; + global_params.mask |= KADM5_CONFIG_DBNAME; + break; + case 'a': + global_params.admin_dbname = optarg; + global_params.mask |= KADM5_CONFIG_ADBNAME; + break; + case 'r': + global_params.realm = optarg; + global_params.mask |= KADM5_CONFIG_REALM; + /* not sure this is really necessary */ + if ((retval = krb5_set_default_realm(util_context, + global_params.realm))) { + com_err(progname, retval, "while setting default realm name"); + exit(1); + } + break; + case 'R': + request = optarg; + break; + case 'k': + if (krb5_string_to_enctype(optarg, &global_params.enctype)) + com_err(argv[0], 0, "%s is an invalid enctype", optarg); + global_params.mask |= KADM5_CONFIG_ENCTYPE; + break; + case 'M': /* master key name in DB */ + global_params.mkey_name = optarg; + global_params.mask |= KADM5_CONFIG_MKEY_NAME; + break; + case 'm': + manual_mkey = TRUE; + global_params.mkey_from_kbd = 1; + global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + break; + case 's': + /* Open the script file */ + if (!(scriptfile = fopen(optarg, "r"))) { + com_err(argv[0], errno, "while opening script file %s", + optarg); + exit(1); + } + break; + case 'f': + global_params.stash_file = optarg; + global_params.mask |= KADM5_CONFIG_STASH_FILE; + break; + case '?': + default: + usage(progname, 1); + /*NOTREACHED*/ + } + } + + if (retval = kadm5_get_config_params(util_context, NULL, NULL, + &global_params, &global_params)) { + com_err(argv[0], retval, "while retreiving configuration parameters"); + exit(1); + } + + /* + * Dump creates files which should not be world-readable. It is + * easiest to do a single umask call here; any shells run by the + * ss command interface will have umask = 77 but that is not a + * serious problem. + */ + (void) umask(077); + + master_keyblock.enctype = global_params.enctype; + if (master_keyblock.enctype != ENCTYPE_UNKNOWN) { + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, + tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + krb5_use_enctype(util_context, &master_encblock, + master_keyblock.enctype); + } + + + open_db_and_mkey(); + + exit_status = 0; /* It's OK if we get errors in open_db_and_mkey */ + return request; +} + +#if 0 +/* + * This function is no longer used in kdb5_util (and it would no + * longer work, anyway). + */ +void set_dbname(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc < 3) { + com_err(argv[0], 0, "Too few arguments"); + com_err(argv[0], 0, "Usage: %s dbpathname realmname", argv[0]); + exit_status++; + return; + } + if (dbactive) { + if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) { + com_err(argv[0], retval, "while closing previous database"); + exit_status++; + return; + } + if (valid_master_key) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &master_random); + memset((char *)master_keyblock.contents, 0, + master_keyblock.length); + krb5_xfree(master_keyblock.contents); + master_keyblock.contents = NULL; + valid_master_key = 0; + } + krb5_free_principal(util_context, master_princ); + dbactive = FALSE; + } + + (void) set_dbname_help(argv[0], argv[1]); + return; +} +#endif + +/* + * open_db_and_mkey: Opens the KDC and policy database, and sets the + * global master_* variables. Sets dbactive to TRUE if the databases + * are opened, and valid_master_key to 1 if the global master + * variables are set properly. Returns 0 on success, and 1 on + * failure, but it is not considered a failure if the master key + * cannot be fetched (the master key stash file may not exist when the + * program is run). + */ +int open_db_and_mkey() +{ + krb5_error_code retval; + int nentries, i; + krb5_boolean more; + krb5_data scratch, pwd; + + dbactive = FALSE; + valid_master_key = 0; + + if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { + com_err(progname, retval, "while setting active database to '%s'", + global_params.dbname); + exit_status++; + return(1); + } + if ((retval = krb5_db_init(util_context))) { + com_err(progname, retval, "while initializing database"); + exit_status++; + return(1); + } + if (retval = osa_adb_open_policy(&policy_db, &global_params)) { + com_err(progname, retval, "opening policy database"); + exit_status++; + return (1); + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + 0, &master_princ))) { + com_err(progname, retval, "while setting up master key name"); + exit_status++; + return(1); + } + nentries = 1; + if ((retval = krb5_db_get_principal(util_context, master_princ, + &master_entry, &nentries, &more))) { + com_err(progname, retval, "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } else if (more) { + com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE, + "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } else if (!nentries) { + com_err(progname, KRB5_KDB_NOENTRY, "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } + + krb5_db_free_principal(util_context, &master_entry, nentries); + + /* the databases are now open, and the master principal exists */ + dbactive = TRUE; + + if (mkey_password) { + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &scratch); + if (retval) { + com_err(progname, retval, "while calculated master key salt"); + return(1); + } + + /* If no encryption type is set, use the default */ + if (master_keyblock.enctype == ENCTYPE_UNKNOWN) { + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, + tmp, sizeof(tmp))) + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + krb5_use_enctype(util_context, &master_encblock, + master_keyblock.enctype); + } + + retval = krb5_string_to_key(util_context, &master_encblock, + &master_keyblock, &pwd, &scratch); + if (retval) { + com_err(progname, retval, + "while transforming master key from password"); + return(1); + } + free(scratch.data); + mkey_password = 0; + } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, + &master_encblock, manual_mkey, + FALSE, global_params.stash_file, + 0, &master_keyblock))) { + com_err(progname, retval, "while reading master key"); + com_err(progname, 0, "Warning: proceeding without master key"); + exit_status++; + return(0); + } + if ((retval = krb5_db_verify_master_key(util_context, master_princ, + &master_keyblock,&master_encblock)) + ) { + com_err(progname, retval, "while verifying master key"); + exit_status++; + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + if ((retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock))) { + com_err(progname, retval, "while processing master key"); + exit_status++; + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + if ((retval = krb5_init_random_key(util_context, &master_encblock, + &master_keyblock, + &master_random))) { + com_err(progname, retval, "while initializing random key generator"); + exit_status++; + (void) krb5_finish_key(util_context, &master_encblock); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + + valid_master_key = 1; + dbactive = TRUE; + return 0; +} + +#ifdef HAVE_GETCWD +#undef getwd +#endif + +int +quit() +{ + krb5_error_code retval; + static krb5_boolean finished = 0; + + if (finished) + return 0; + if (valid_master_key) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &master_random); + } + retval = krb5_db_fini(util_context); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + finished = TRUE; + if (retval && retval != KRB5_KDB_DBNOTINITED) { + com_err(progname, retval, "while closing database"); + exit_status++; + return 1; + } + return 0; +} diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h new file mode 100644 index 000000000..b580e2f6a --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.h @@ -0,0 +1,49 @@ +/* + * admin/edit/kdb5_edit.h + * + * Copyright 1992 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. + * + */ + +#define REALM_SEP '@' +#define REALM_SEP_STR "@" + +extern char *progname; +extern char *Err_no_database; + +void add_key + PROTOTYPE((char const *, char const *, + krb5_const_principal, const krb5_keyblock *, + krb5_kvno, krb5_keysalt *)); +int set_dbname_help + PROTOTYPE((char *, char *)); + +char *kdb5_util_Init PROTOTYPE((int, char **)); + +int quit(); + +int check_for_match + PROTOTYPE((char *, int, krb5_db_entry *, int, int)); + +void parse_token + PROTOTYPE((char *, int *, int *, char *)); + +int create_db_entry + PROTOTYPE((krb5_principal, krb5_db_entry *)); diff --git a/src/kadmin/dbutil/kdb5_util_ct.ct b/src/kadmin/dbutil/kdb5_util_ct.ct new file mode 100644 index 000000000..bac1df125 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util_ct.ct @@ -0,0 +1,56 @@ +# admin/edit/kdb5_ed_ct.ct +# +# Copyright 1990 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. +# +# +# Command table for Kerberos administration edit +# + +command_table kdb5_edit_cmds; + +request kdb5_create, "Create a new Kerberos database", + create_db, create; + +request kdb5_destroy, "Destroy a Kerberos database", + destroy_db, destroy; + +request kdb5_stash, "Stash the Kerberos master key", + stash_mkey, stash; + +request dump_db, "Dump database entries to a file", + dump_db, ddb; + +request dump_v4db, "Dump database entries to a V4 slave dump file", + dump_v4db, d4db; + +request load_db, "Load database entries from a file", + load_db, lddb; + +request load_v4db, "Load database entries from a V4 slave dump file", + load_v4db, lddb4; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; diff --git a/src/kadmin/dbutil/loadv4.c b/src/kadmin/dbutil/loadv4.c new file mode 100644 index 000000000..a1d37edc7 --- /dev/null +++ b/src/kadmin/dbutil/loadv4.c @@ -0,0 +1,881 @@ +/* + * admin/edit/loadv4.c + * + * Copyright 1990,1991 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. + * + * + * Generate (from scratch) a Kerberos V5 KDC database, filling it in with the + * entries from a V4 database. + */ + +#ifdef KRB5_KRB4_COMPAT + +#include +#include +#include +/* MKEYFILE is now defined in kdc.h */ +#include + +static C_Block master_key; +static Key_schedule master_key_schedule; +static long master_key_version; + +static char *v4_mkeyfile = "/.k"; + +#include "k5-int.h" +#include "com_err.h" +#include "adm.h" +#include "adm_proto.h" +#include + +#include /* ntohl */ + +#define PROGNAME argv[0] + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + RANDOM_KEY /* choose a random key */ +}; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; +}; + +static struct realm_info rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + 0 +}; + +static int verbose = 0; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +static int v4init PROTOTYPE((char *, char *, int, char *)); +static krb5_error_code enter_in_v5_db PROTOTYPE((krb5_context, + char *, Principal *)); +static krb5_error_code process_v4_dump PROTOTYPE((krb5_context, char *, + char *)); +static krb5_error_code fixup_database PROTOTYPE((krb5_context, char *)); + +static int create_local_tgt = 0; + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] [-k enctype]\n\ +\t[-M mkeyname] -f inputfile\n", + who); + return; +} + +static krb5_keyblock master_keyblock; +static krb5_principal master_princ; +static krb5_encrypt_block master_encblock; + +static krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +static krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +static krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +static krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + + +void +load_v4db(argc, argv) +int argc; +char *argv[]; +{ + krb5_error_code retval; + /* The kdb library will default to this, but it is convenient to + make it explicit (error reporting and temporary filename generation + use it). */ + char *dbname = DEFAULT_KDB_FILE; + char *v4dbname = 0; + char *v4dumpfile = 0; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *defrealm; + int enctypedone = 0; + int v4manual = 0; + int read_mkey = 0; + int tempdb = 0; + char *tempdbname; + krb5_context context; + char *stash_file = (char *) NULL; + krb5_realm_params *rparams; + int persist, op_ind; + + krb5_init_context(&context); + + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + persist = 1; + op_ind = 1; + while (persist && (op_ind < argc)) { + if (!strcmp(argv[op_ind], "-d") && ((argc - op_ind) >= 2)) { + dbname = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-T")) { + create_local_tgt = 1; + } + else if (!strcmp(argv[op_ind], "-t")) { + tempdb = 1; + } + else if (!strcmp(argv[op_ind], "-r") && ((argc - op_ind) >= 2)) { + realm = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-K")) { + read_mkey = 1; + } + else if (!strcmp(argv[op_ind], "-v")) { + verbose = 1; + } + else if (!strcmp(argv[op_ind], "-k") && ((argc - op_ind) >= 2)) { + if (!krb5_string_to_enctype(argv[op_ind+1], + &master_keyblock.enctype)) + enctypedone++; + else + com_err(argv[0], 0, "%s is an invalid enctype", + argv[op_ind+1]); + op_ind++; + } + else if (!strcmp(argv[op_ind], "-M") && ((argc - op_ind) >= 2)) { + mkey_name = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-n")) { + v4manual++; + } + else if (!strcmp(argv[op_ind], "-f") && ((argc - op_ind) >= 2)) { + if (v4dbname) { + usage(PROGNAME, 1); + return; + } + v4dumpfile = argv[op_ind+1]; + op_ind++; + } + else + persist = 0; + op_ind++; + } + + /* + * Attempt to read the KDC profile. If we do, then read appropriate values + * from it and augment values supplied on the command line. + */ + if (!(retval = krb5_read_realm_params(context, + realm, + (char *) NULL, + (char *) NULL, + &rparams))) { + /* Get the value for the database */ + if (rparams->realm_dbname && !dbname) + dbname = strdup(rparams->realm_dbname); + + /* Get the value for the master key name */ + if (rparams->realm_mkey_name && !mkey_name) + mkey_name = strdup(rparams->realm_mkey_name); + + /* Get the value for the master key type */ + if (rparams->realm_enctype_valid && !enctypedone) { + master_keyblock.enctype = rparams->realm_enctype; + enctypedone++; + } + + /* Get the value for the stashfile */ + if (rparams->realm_stash_file) + stash_file = strdup(rparams->realm_stash_file); + + /* Get the value for maximum ticket lifetime. */ + if (rparams->realm_max_life_valid) + rblock.max_life = rparams->realm_max_life; + + /* Get the value for maximum renewable ticket lifetime. */ + if (rparams->realm_max_rlife_valid) + rblock.max_rlife = rparams->realm_max_rlife; + + /* Get the value for the default principal expiration */ + if (rparams->realm_expiration_valid) + rblock.expiration = rparams->realm_expiration; + + /* Get the value for the default principal flags */ + if (rparams->realm_flags_valid) + rblock.flags = rparams->realm_flags; + + krb5_free_realm_params(context, rparams); + } + + if (!v4dumpfile) { + usage(PROGNAME, 1); + return; + } + + if (!enctypedone) + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + + if (!valid_enctype(master_keyblock.enctype)) { + com_err(PROGNAME, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + return; + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + /* If the user has not requested locking, don't modify an existing database. */ + if (! tempdb) { + retval = krb5_db_set_name(context, dbname); + if (retval != ENOENT) { + fprintf(stderr, + "%s: The v5 database appears to already exist.\n", + PROGNAME); + return; + } + tempdbname = dbname; + } else { + int dbnamelen = strlen(dbname); + tempdbname = malloc(dbnamelen + 2); + if (tempdbname == 0) { + com_err(PROGNAME, ENOMEM, "allocating temporary filename"); + return; + } + strcpy(tempdbname, dbname); + tempdbname[dbnamelen] = '~'; + tempdbname[dbnamelen+1] = 0; + (void) kdb5_db_destroy(context, tempdbname); + } + + + if (!realm) { + if (retval = krb5_get_default_realm(context, &defrealm)) { + com_err(PROGNAME, retval, "while retrieving default realm name"); + return; + } + realm = defrealm; + } + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ)) { + com_err(PROGNAME, retval, "while setting up master key name"); + return; + } + + krb5_princ_set_realm_data(context, &db_create_princ, realm); + krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm)); + krb5_princ_set_realm_data(context, &tgt_princ, realm); + krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm)); + krb5_princ_component(context, &tgt_princ,1)->data = realm; + krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + dbname, realm, mkey_fullname); + + if (read_mkey) { + puts("You will be prompted for the version 5 database Master Password."); + puts("It is important that you NOT FORGET this password."); + fflush(stdout); + } + + if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock, + read_mkey, read_mkey, stash_file, 0, + &master_keyblock)) { + com_err(PROGNAME, retval, "while reading master key"); + return; + } + if (retval = krb5_process_key(context, &master_encblock, &master_keyblock)) { + com_err(PROGNAME, retval, "while processing master key"); + return; + } + + rblock.eblock = &master_encblock; + if (retval = krb5_init_random_key(context, &master_encblock, + &master_keyblock, &rblock.rseed)) { + com_err(PROGNAME, retval, "while initializing random key generator"); + (void) krb5_finish_key(context, &master_encblock); + return; + } + if (retval = krb5_db_create(context, tempdbname)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while creating %sdatabase '%s'", + tempdb ? "temporary " : "", tempdbname); + return; + } + if (retval = krb5_db_set_name(context, tempdbname)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while setting active database to '%s'", + tempdbname); + return; + } + if (v4init(PROGNAME, v4dbname, v4manual, v4dumpfile)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + return; + } + if ((retval = krb5_db_init(context)) || + (retval = krb5_dbm_db_open_database(context))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while initializing the database '%s'", + tempdbname); + return; + } + + if (retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while adding K/M to the database"); + return; + } + + if (create_local_tgt && + (retval = add_principal(context, &tgt_princ, RANDOM_KEY, &rblock))) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while adding TGT service to the database"); + return; + } + + retval = process_v4_dump(context, v4dumpfile, realm); + putchar('\n'); + if (retval) + com_err(PROGNAME, retval, "while translating entries to the database"); + else { + retval = fixup_database(context, realm); + } + + /* clean up; rename temporary database if there were no errors */ + if (retval == 0) { + if (retval = krb5_db_fini (context)) + com_err(PROGNAME, retval, "while shutting down database"); + else if (tempdb && (retval = krb5_dbm_db_rename(context, tempdbname, + dbname))) + com_err(PROGNAME, retval, "while renaming temporary database"); + } else { + (void) krb5_db_fini (context); + if (tempdb) + (void) krb5_dbm_db_destroy (context, tempdbname); + } + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_free_context(context); + return; +} + +static int +v4init(pname, name, manual, dumpfile) +char *pname, *name; +int manual; +char *dumpfile; +{ + int fd; + int ok = 0; + + if (!manual) { + fd = open(v4_mkeyfile, O_RDONLY, 0600); + if (fd >= 0) { + if (read(fd, master_key, sizeof(master_key)) == sizeof(master_key)) + ok = 1; + close(fd); + } + } + if (!ok) { + des_read_password(master_key, "V4 Kerberos master key: ", 0); + printf("\n"); + } + key_sched(master_key, master_key_schedule); + return 0; +} + +static krb5_error_code +enter_in_v5_db(context, realm, princ) +krb5_context context; +char *realm; +Principal *princ; +{ + krb5_db_entry entry; + krb5_error_code retval; + krb5_keyblock v4v5key; + int nentries = 1; + des_cblock v4key; + char *name; + krb5_timestamp mod_time; + krb5_principal mod_princ; + krb5_keysalt keysalt; + + /* don't convert local TGT if we created a TGT already.... */ + if (create_local_tgt && !strcmp(princ->name, "krbtgt") && + !strcmp(princ->instance, realm)) { + if (verbose) + printf("\nignoring local TGT: '%s.%s' ...", + princ->name, princ->instance); + return 0; + } + if (!strcmp(princ->name, KERB_M_NAME) && + !strcmp(princ->instance, KERB_M_INST)) { + des_cblock key_from_db; + int val; + + /* here's our chance to verify the master key */ + /* + * use the master key to decrypt the key in the db, had better + * be the same! + */ + memcpy(key_from_db, (char *)&princ->key_low, 4); + memcpy(((char *) key_from_db) + 4, (char *)&princ->key_high, 4); + pcbc_encrypt((C_Block *) &key_from_db, + (C_Block *) &key_from_db, + (long) sizeof(C_Block), + master_key_schedule, + (C_Block *) master_key, + DECRYPT); + val = memcmp((char *) master_key, (char *) key_from_db, + sizeof(master_key)); + memset((char *)key_from_db, 0, sizeof(key_from_db)); + if (val) { + return KRB5_KDB_BADMASTERKEY; + } + if (verbose) + printf("\nignoring '%s.%s' ...", princ->name, princ->instance); + return 0; + } + memset((char *) &entry, 0, sizeof(entry)); + if (retval = krb5_425_conv_principal(context, princ->name, princ->instance, + realm, &entry.princ)) + return retval; + if (verbose) { + if (retval = krb5_unparse_name(context, entry.princ, &name)) + name = strdup(""); + if (verbose) + printf("\ntranslating %s...", name); + free(name); + } + + if (retval = krb5_build_principal(context, &mod_princ, + strlen(realm), + realm, princ->mod_name, + princ->mod_instance[0] ? princ->mod_instance : 0, + 0)) { + krb5_free_principal(context, entry.princ); + return retval; + } + mod_time = princ->mod_date; + + entry.max_life = princ->max_life * 60 * 5; + entry.max_renewable_life = rblock.max_rlife; + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.expiration = princ->exp_date; + entry.attributes = rblock.flags; /* XXX is there a way to convert + the old attrs? */ + + memcpy((char *)v4key, (char *)&(princ->key_low), 4); + memcpy((char *) (((char *) v4key) + 4), (char *)&(princ->key_high), 4); + pcbc_encrypt((C_Block *) &v4key, + (C_Block *) &v4key, + (long) sizeof(C_Block), + master_key_schedule, + (C_Block *) master_key, + DECRYPT); + + v4v5key.magic = KV5M_KEYBLOCK; + v4v5key.contents = (krb5_octet *)v4key; + v4v5key.enctype = ENCTYPE_DES_CBC_CRC; + v4v5key.length = sizeof(v4key); + + retval = krb5_dbe_create_key_data(context, &entry); + if (retval) { + krb5_free_principal(context, entry.princ); + krb5_free_principal(context, mod_princ); + return retval; + } + + keysalt.type = KRB5_KDB_SALTTYPE_V4; + keysalt.data.length = 0; + keysalt.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(context, rblock.eblock, + &v4v5key, &keysalt, + princ->key_version, + &entry.key_data[0]); + if (!retval) + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, mod_princ); + if (retval) { + krb5_db_free_principal(context, &entry, 1); + krb5_free_principal(context, mod_princ); + return retval; + } + memset((char *)v4key, 0, sizeof(v4key)); + + retval = krb5_db_put_principal(context, &entry, &nentries); + + if (!retval && !strcmp(princ->name, "krbtgt") && + strcmp(princ->instance, realm) && princ->instance[0]) { + krb5_free_principal(context, entry.princ); + if (retval = krb5_build_principal(context, &entry.princ, + strlen(princ->instance), + princ->instance, + "krbtgt", realm, 0)) + return retval; + retval = krb5_db_put_principal(context, &entry, &nentries); + } + + krb5_db_free_principal(context, &entry, 1); + krb5_free_principal(context, mod_princ); + + return retval; +} + +static krb5_error_code +add_principal(context, princ, op, pblock) +krb5_context context; +krb5_principal princ; +enum ap_op op; +struct realm_info *pblock; +{ + krb5_db_entry entry; + krb5_error_code retval; + krb5_keyblock *rkey; + int nentries = 1; + krb5_timestamp mod_time; + krb5_principal mod_princ; + + memset((char *) &entry, 0, sizeof(entry)); + if (retval = krb5_copy_principal(context, princ, &entry.princ)) + return(retval); + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.expiration = pblock->expiration; + + if ((retval = krb5_timeofday(context, &mod_time))) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + entry.attributes = pblock->flags; + + if (retval = krb5_dbe_create_key_data(context, &entry)) { + krb5_db_free_principal(context, &entry, 1); + return(retval); + } + + switch (op) { + case MASTER_KEY: + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, + (krb5_keysalt *) NULL, 1, + &entry.key_data[0])) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + break; + case RANDOM_KEY: + if (retval = krb5_random_key(context, pblock->eblock, pblock->rseed, + &rkey)) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + rkey, + (krb5_keysalt *) NULL, 1, + &entry.key_data[0])) { + krb5_db_free_principal(context, &entry, 1); + return(retval); + } + krb5_free_keyblock(context, rkey); + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, &db_create_princ); + if (!retval) + retval = krb5_db_put_principal(context, &entry, &nentries); + krb5_db_free_principal(context, &entry, 1); + return retval; +} + +/* + * Convert a struct tm * to a UNIX time. + */ + + +#define daysinyear(y) (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366))) + +#define SECSPERDAY 24*60*60 +#define SECSPERHOUR 60*60 +#define SECSPERMIN 60 + +static int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + 365}; + +static int leapyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static int nonleapyear[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static long +maketime(tp, local) +register struct tm *tp; +int local; +{ + register long retval; + int foo; + int *marray; + + if (tp->tm_mon < 0 || tp->tm_mon > 11 || + tp->tm_hour < 0 || tp->tm_hour > 23 || + tp->tm_min < 0 || tp->tm_min > 59 || + tp->tm_sec < 0 || tp->tm_sec > 59) /* out of range */ + return 0; + + retval = 0; + if (tp->tm_year < 1900) + foo = tp->tm_year + 1900; + else + foo = tp->tm_year; + + if (foo < 1901 || foo > 2038) /* year is too small/large */ + return 0; + + if (daysinyear(foo) == 366) { + if (tp->tm_mon > 1) + retval+= SECSPERDAY; /* add leap day */ + marray = leapyear; + } else + marray = nonleapyear; + + if (tp->tm_mday < 0 || tp->tm_mday > marray[tp->tm_mon]) + return 0; /* out of range */ + + while (--foo >= 1970) + retval += daysinyear(foo) * SECSPERDAY; + + retval += cumdays[tp->tm_mon] * SECSPERDAY; + retval += (tp->tm_mday-1) * SECSPERDAY; + retval += tp->tm_hour * SECSPERHOUR + tp->tm_min * SECSPERMIN + tp->tm_sec; + + if (local) { + /* need to use local time, so we retrieve timezone info */ + struct timezone tz; + struct timeval tv; + if (gettimeofday(&tv, &tz) < 0) { + /* some error--give up? */ + return(retval); + } + retval += tz.tz_minuteswest * SECSPERMIN; + } + return(retval); +} + +static long +time_explode(cp) +register char *cp; +{ + char wbuf[5]; + struct tm tp; + int local; + + memset((char *)&tp, 0, sizeof(tp)); + + if (strlen(cp) > 10) { /* new format */ + (void) strncpy(wbuf, cp, 4); + wbuf[4] = 0; + tp.tm_year = atoi(wbuf); + cp += 4; /* step over the year */ + local = 0; /* GMT */ + } else { /* old format: local time, + year is 2 digits, assuming 19xx */ + wbuf[0] = *cp++; + wbuf[1] = *cp++; + wbuf[2] = 0; + tp.tm_year = 1900 + atoi(wbuf); + local = 1; /* local */ + } + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + wbuf[2] = 0; + tp.tm_mon = atoi(wbuf)-1; + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_mday = atoi(wbuf); + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_hour = atoi(wbuf); + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_min = atoi(wbuf); + + + return(maketime(&tp, local)); +} + +static krb5_error_code +process_v4_dump(context, dumpfile, realm) +krb5_context context; +char *dumpfile; +char *realm; +{ + krb5_error_code retval; + FILE *input_file; + Principal aprinc; + char exp_date_str[50]; + char mod_date_str[50]; + int temp1, temp2, temp3; + long time_explode(); + + input_file = fopen(dumpfile, "r"); + if (!input_file) + return errno; + + for (;;) { /* explicit break on eof from fscanf */ + int nread; + + memset((char *)&aprinc, 0, sizeof(aprinc)); + nread = fscanf(input_file, + "%s %s %d %d %d %hd %x %x %s %s %s %s\n", + aprinc.name, + aprinc.instance, + &temp1, + &temp2, + &temp3, + &aprinc.attributes, + &aprinc.key_low, + &aprinc.key_high, + exp_date_str, + mod_date_str, + aprinc.mod_name, + aprinc.mod_instance); + if (nread != 12) { + retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT; + break; + } + aprinc.key_low = ntohl (aprinc.key_low); + aprinc.key_high = ntohl (aprinc.key_high); + aprinc.max_life = (unsigned char) temp1; + aprinc.kdc_key_ver = (unsigned char) temp2; + aprinc.key_version = (unsigned char) temp3; + aprinc.exp_date = time_explode(exp_date_str); + aprinc.mod_date = time_explode(mod_date_str); + if (aprinc.instance[0] == '*') + aprinc.instance[0] = '\0'; + if (aprinc.mod_name[0] == '*') + aprinc.mod_name[0] = '\0'; + if (aprinc.mod_instance[0] == '*') + aprinc.mod_instance[0] = '\0'; + if (retval = enter_in_v5_db(context, realm, &aprinc)) + break; + } + (void) fclose(input_file); + return retval; +} + +static krb5_error_code fixup_database(context, realm) + krb5_context context; + char * realm; +{ + krb5_db_entry entry; + krb5_error_code retval; + int nprincs; + krb5_boolean more; + + nprincs = 1; + if (retval = krb5_db_get_principal(context, &tgt_princ, &entry, + &nprincs, &more)) + return retval; + + if (nprincs == 0) + return 0; + + entry.attributes |= KRB5_KDB_SUPPORT_DESMD5; + + retval = krb5_db_put_principal(context, &entry, &nprincs); + + if (nprincs) + krb5_db_free_principal(context, &entry, nprincs); + + return retval; +} + +#else /* KRB5_KRB4_COMPAT */ +void +load_v4db(argc, argv) + int argc; + char *argv[]; +{ + printf("This version of krb5_edit does not support the V4 load command.\n"); +} +#endif /* KRB5_KRB4_COMPAT */ diff --git a/src/kadmin/dbutil/ss_wrapper.c b/src/kadmin/dbutil/ss_wrapper.c new file mode 100644 index 000000000..ada85efc9 --- /dev/null +++ b/src/kadmin/dbutil/ss_wrapper.c @@ -0,0 +1,85 @@ +/* + * admin/edit/ss_wrapper.c + * + * Copyright 1990,1991 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. + * + * + * ss wrapper for kdb5_edit + */ + +#include +#include "kdb5_util.h" +#include +#include + +extern ss_request_table kdb5_edit_cmds; +extern int exit_status; +extern FILE *scriptfile; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + request = kdb5_util_Init(argc, argv); + sci_idx = ss_create_invocation("kdb5_util", "5.0", (char *) NULL, + &kdb5_edit_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + + if (request) { + code = ss_execute_line(sci_idx, request, &code); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else if (scriptfile) { + char *command; + int nread; + + /* Get a buffer */ + if ((command = (char *) malloc(BUFSIZ))) { + /* Process commands from the script until end-of-file or error */ + while (!feof(scriptfile) && + (fgets(command, BUFSIZ, scriptfile))) { + + /* Strip trailing newline */ + if (command[strlen(command)-1] == '\n') + command[strlen(command)-1] = '\0'; + + /* Execute the command */ + code = ss_execute_line(sci_idx, command, &code); + if (code != 0) { + ss_perror(sci_idx, code, command); + exit_status++; + break; + } + } + free(command); + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/dbutil/string_table.c b/src/kadmin/dbutil/string_table.c new file mode 100644 index 000000000..b9f86a363 --- /dev/null +++ b/src/kadmin/dbutil/string_table.c @@ -0,0 +1,91 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* String table of messages for kadm5_create */ + +char *str_INITING_KCONTEXT = "while initializing the kerberos context"; + +char *str_PARSE_NAME = "while parsing admin principal name."; + +char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name."; + +char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists."; + +char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists."; + +char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists."; + +char *str_ADMIN_PRINC_WRONG_ATTRS = + "Warning! Admin principal has incorrect attributes.\n" + "\tDISALLOW_TGT should be set, and max_life should be three hours.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_CHANGEPW_PRINC_WRONG_ATTRS = + "Warning! Changepw principal has incorrect attributes.\n" + "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and " + "max_life should be five minutes.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_HISTORY_PRINC_WRONG_ATTRS = + "Warning! Admin history principal has incorrect attributes.\n" + "\tDISALLOW_ALL_TIX should be set.\n" + "\tThis program will leave it as-is, but beware!."; + +char *str_CREATED_PRINC_DB = + "%s: Admin principal database created (or it already existed).\n"; /* whoami */ + +char *str_CREATED_POLICY_DB = + "%s: Admin policy database created (or it already existed).\n"; /* whoami */ + +char *str_RANDOM_KEY = + "while calling random key for %s."; /* principal name */ + +char *str_ENCRYPT_KEY = + "while calling encrypt key for %s."; /* principal name */ + +char *str_PUT_PRINC = + "while calling storing %s in Kerberos database."; /* principal name */ + +char *str_CREATING_POLICY_DB = "while creating/opening admin policy database."; + +char *str_CLOSING_POLICY_DB = "while closing admin policy database."; + +char *str_CREATING_PRINC_DB = "while creating/opening admin principal database."; + +char *str_CLOSING_PRINC_DB = "while closing admin principal database."; + +char *str_CREATING_PRINC_ENTRY = + "while creating admin principal database entry for %s."; /* princ_name */ + +char *str_A_PRINC = "a principal"; + +char *str_UNPARSE_PRINC = "while unparsing principal."; + +char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */ + +char *str_INIT_KDB = "while initializing kdb."; + +char *str_NO_KDB = +"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\ +If you haven't run kdb5_create you need to do so before running this command."; + + +char *str_INIT_RANDOM_KEY = "while initializing random key generator."; + +char *str_TOO_MANY_ADMIN_PRINC = + "while fetching admin princ. Can only have one admin principal."; + +char *str_TOO_MANY_CHANGEPW_PRINC = + "while fetching changepw princ. Can only have one changepw principal."; + +char *str_TOO_MANY_HIST_PRINC = + "while fetching history princ. Can only have one history principal."; + +char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets."; diff --git a/src/kadmin/dbutil/string_table.h b/src/kadmin/dbutil/string_table.h new file mode 100644 index 000000000..e8cb45367 --- /dev/null +++ b/src/kadmin/dbutil/string_table.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + */ + +#ifndef _OVSEC_ADM_STRINGS_ + +extern char *str_INITING_KCONTEXT; +extern char *str_PARSE_NAME; +extern char *str_HISTORY_PARSE_NAME; +extern char *str_ADMIN_PRINC_EXISTS; +extern char *str_CHANGEPW_PRINC_EXISTS; +extern char *str_HISTORY_PRINC_EXISTS; +extern char *str_ADMIN_PRINC_WRONG_ATTRS; +extern char *str_CHANGEPW_PRINC_WRONG_ATTRS; +extern char *str_HISTORY_PRINC_WRONG_ATTRS; +extern char *str_CREATED_PRINC_DB; +extern char *str_CREATED_POLICY_DB; +extern char *str_RANDOM_KEY; +extern char *str_ENCRYPT_KEY; +extern char *str_PUT_PRINC; +extern char *str_CREATING_POLICY_DB; +extern char *str_CLOSING_POLICY_DB; +extern char *str_CREATING_PRINC_DB; +extern char *str_CLOSING_PRINC_DB; +extern char *str_CREATING_PRINC_ENTRY; +extern char *str_A_PRINC; +extern char *str_UNPARSE_PRINC; +extern char *str_CREATED_PRINC; +extern char *str_INIT_KDB; +extern char *str_NO_KDB; +extern char *str_INIT_RANDOM_KEY; +extern char *str_TOO_MANY_ADMIN_PRINC; +extern char *str_TOO_MANY_CHANGEPW_PRINC; +extern char *str_TOO_MANY_HIST_PRINC; +extern char *str_WHILE_DESTROYING_ADMIN_SESSION; + +#endif /* _OVSEC_ADM_STRINGS_ */ diff --git a/src/kadmin/dbutil/tcl_wrapper.c b/src/kadmin/dbutil/tcl_wrapper.c new file mode 100644 index 000000000..d527fa0d1 --- /dev/null +++ b/src/kadmin/dbutil/tcl_wrapper.c @@ -0,0 +1,235 @@ +/* + * admin/edit/tcl_wrapper.c + * + * Copyright 1990,1991 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. + * + * + * Tcl wrapper for kdb5_edit + */ + +#include "k5-int.h" +#include "kdb5_edit.h" +#include + +#define CMDDECL(x) int x(clientData, interp, argc, argv)\ + ClientData clientData;\ + Tcl_Interp * interp;\ + int argc;\ + char ** argv; +#define CMDPROTO(x) int x PROTOTYPE((ClientData, Tcl_Interp,\ + int, char **)) +#define MKCMD(name,cmd) Tcl_CreateCommand(interp, name, cmd,\ + (ClientData)NULL,\ + (Tcl_CmdDeleteProc *)NULL) + +extern int main(); +int *tclDummyMainPtr = (int *) main; /* force ld to suck in main() + from libtcl.a */ +extern Tcl_Interp *interp; /* XXX yes, this is gross, + but we do need it for some things */ +extern int exit_status; + +void show_principal PROTOTYPE((int, char **)); +void add_new_key PROTOTYPE((int, char **)); +void change_pwd_key PROTOTYPE((int, char **)); +void add_rnd_key PROTOTYPE((int, char **)); +void change_rnd_key PROTOTYPE((int, char **)); +void delete_entry PROTOTYPE((int, char **)); +void extract_srvtab PROTOTYPE((krb5_context, int, char **)); +void extract_v4_srvtab PROTOTYPE((int, char **)); +void list_db PROTOTYPE((int, char **)); +void dump_db PROTOTYPE((int, char **)); +void load_db PROTOTYPE((int, char **)); +void set_dbname PROTOTYPE((krb5_context, int, char **)); +void enter_master_key PROTOTYPE((krb5_context, int, char **)); + +/* + * this is mostly stolen from tcl_ExitCmd() + * we need to do a few extra things, though... + */ +int doquit(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + int value; + + if ((argc != 1) && (argc != 2)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ?returnCode?\"", (char *) NULL); + return TCL_ERROR; + } + if (argc == 1) { + exit(quit() ? 1 : exit_status); + } + if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) { + return TCL_ERROR; + } + (void)quit(); + exit(value); + /*NOTREACHED*/ + return TCL_OK; /* Better not ever reach this! */ +} + +int list_requests(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + Tcl_SetResult(interp, "show_principal, show: Show the Kerberos database entry for a principal\nadd_new_key, ank: Add new entry to the Kerberos database (prompting for password\nchange_pwd_key, cpw: Change key of an entry in the Kerberos database (prompting for password)\nadd_rnd_key, ark: Add new entry to Kerberos database, using a random key\nchange_rnd_key, crk: Change key of an entry in the Kerberos database (select a random key)\ndelete_entry, delent: Delete an entry from the database\nextract_srvtab, xst, ex_st: Extract service key table\nextract_v4_srvtab, xst4: Extract service key table\nlist_db, ldb: List database entries\nset_dbname, sdbn: Change database name\nenter_master_key, emk: Enter the master key for a database\nchange_working_directory, cwd, cd: Change working directory\nprint_working_directory, pwd: Print working directory\nlist_requests, lr: List available requests\nquit, exit: Exit program", TCL_STATIC); + return TCL_OK; +} + +int wrapper(func, interp, argc, argv) + void (*func)(); + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + (*func)(argc, argv); + return TCL_OK; +} + +int Tcl_AppInit(interp) + Tcl_Interp *interp; +{ + int argc; + char **argv, **mostly_argv; + char *interp_argv, *interp_argv0, *request; + Tcl_CmdInfo cmdInfo; + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + /* + * the following is, admittedly, sorta gross, but the only way + * to grab the original argc, argv once the interpreter is running + */ + interp_argv = Tcl_GetVar(interp, "argv", 0); + if (interp_argv == NULL) + return TCL_ERROR; + else if (Tcl_SplitList(interp, interp_argv, + &argc, &mostly_argv) != TCL_OK) + return TCL_ERROR; + interp_argv0 = Tcl_GetVar(interp, "argv0", 0); + if (interp_argv0 == NULL) + return TCL_ERROR; + if ((argv = (char **)malloc((argc + 1) * sizeof (char *))) == NULL) + return TCL_ERROR; + argv[0] = interp_argv0; + memcpy(argv + 1, mostly_argv, argc++ * sizeof (char *)); + /* + * set up a prompt + */ + if (Tcl_SetVar(interp, "tcl_prompt1", + "puts -nonewline \"kdb5_edit: \"", 0) == NULL) + return TCL_ERROR; + /* + * we don't want arbitrary programs to get exec'd by accident + */ + if (Tcl_SetVar(interp, "auto_noexec", "{}", 0) == NULL) + return TCL_ERROR; + request = kdb5_edit_Init(argc, argv); + Tcl_CallWhenDeleted(interp, doquit, + (ClientData)0); + Tcl_CreateCommand(interp, "quit", doquit, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "exit", doquit, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "list_requests", list_requests, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "lr", list_requests, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + if (Tcl_GetCommandInfo(interp, "cd", &cmdInfo)) { + Tcl_CreateCommand(interp, "cwd", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_working_directory", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + } + if (Tcl_GetCommandInfo(interp, "pwd", &cmdInfo)) { + Tcl_CreateCommand(interp, "print_working_directory", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + } + Tcl_CreateCommand(interp, "show_principal", wrapper, show_principal, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "show", wrapper, show_principal, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "add_new_key", wrapper, add_new_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ank", wrapper, add_new_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_pwd_key", wrapper, change_pwd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "cpw", wrapper, change_pwd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "add_rnd_key", wrapper, add_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ark", wrapper, add_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_rnd_key", wrapper, change_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "crk", wrapper, change_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "delete_entry", wrapper, delete_entry, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "delent", wrapper, delete_entry, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "extract_srvtab", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "xst", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ex_st", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "extract_v4_srvtab", wrapper, extract_v4_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "xv4st", wrapper, extract_v4_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "list_db", wrapper, list_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ldb", wrapper, list_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "dump_db", wrapper, dump_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ddb", wrapper, dump_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "load_db", wrapper, load_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "lddb", wrapper, load_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "set_dbname", wrapper, set_dbname, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "sdbn", wrapper, set_dbname, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "enter_master_key", wrapper, enter_master_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "emk", wrapper, enter_master_key, + (Tcl_CmdDeleteProc *)0); + if (request && (Tcl_Eval(interp, request) == TCL_ERROR)) + return TCL_ERROR; + return TCL_OK; +} diff --git a/src/kadmin/dbutil/util.c b/src/kadmin/dbutil/util.c new file mode 100644 index 000000000..78de2cd6f --- /dev/null +++ b/src/kadmin/dbutil/util.c @@ -0,0 +1,155 @@ +/* + * admin/edit/util.c + * + * Copyright 1992 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. + * + * Utilities for kdb5_edit. + * + * Some routines derived from code contributed by the Sandia National + * Laboratories. Sandia National Laboratories also makes no + * representations about the suitability of the modifications, or + * additions to this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + */ + +#include "k5-int.h" +#include "./kdb5_edit.h" + +#if defined(sysvimp) || ( defined(mips) && defined(SYSTYPE_BSD43)) || (defined(vax) && !defined(ultrix)) +char * +strstr(s1, s2) +char *s1; +char *s2; +{ + int s2len; + int i; + char *temp_ptr; + + temp_ptr = s1; + for ( i = 0; i < strlen(s1); i++) { + if (memcmp(temp_ptr, s2, strlen(s2)) == 0) return(temp_ptr); + temp_ptr += 1; + } + return ((char *) 0); +} +#endif /* sysvimp */ + +void +parse_token(token_in, must_be_first_char, num_tokens, tokens_out) +char *token_in; +int *must_be_first_char; +int *num_tokens; +char *tokens_out; +{ + int i, j; + int token_count = 0; + + i = 0; + j = 0; + + /* Eliminate Up Front Asterisks */ + *must_be_first_char = 1; + for (i = 0; token_in[i] == '*'; i++) { + *must_be_first_char = 0; + } + + if (i == strlen(token_in)) { + *num_tokens = 0; + return; + } + + /* Fill first token_out */ + token_count++; + while ((token_in[i] != '*') && (token_in[i] != '\0')) { + tokens_out[j] = token_in[i]; + j++; + i++; + } + + if (i == strlen(token_in)) { + tokens_out[j] = '\0'; + *num_tokens = token_count; + return; + } + + /* Then All Subsequent Tokens */ + while (i < strlen(token_in)) { + if (token_in[i] == '*') { + token_count++; + tokens_out[j] = '\t'; + } else { + tokens_out[j] = token_in[i]; + } + i++; + j++; + } + tokens_out[j] = '\0'; + + if (tokens_out[j - 1] == '\t') { + token_count--; + tokens_out[j - 1] = '\0'; + } + + *num_tokens = token_count; + return; +} + +int +check_for_match(search_field, must_be_first_character, chk_entry, + num_tokens, type) +int must_be_first_character; +char *search_field; +krb5_db_entry *chk_entry; +int num_tokens; +int type; +{ + char token1[256]; + char *found1; + char token2[256]; + char *found2; + char token3[256]; + char *found3; + char *local_entry; + + local_entry = chk_entry->princ->data[type].data; + + token1[0] = token2[0] = token3[0] = '\0'; + + (void) sscanf(search_field, "%s\t%s\t%s", token1, token2, token3); + + found1 = strstr(local_entry, token1); + + if (must_be_first_character && (found1 != local_entry)) return(0); + + if (found1 && (num_tokens == 1)) return(1); + + if (found1 && (num_tokens > 1)) { + found2 = strstr(local_entry, token2); + if (found2 && (found2 > found1) && (num_tokens == 2)) return(1); + } + + if ((found2 > found1) && (num_tokens == 3)) { + found3 = strstr(local_entry, token3); + if (found3 && (found3 > found2) && (found2 > found1)) return(1); + } + return(0); +} + diff --git a/src/kadmin/export/ChangeLog b/src/kadmin/export/ChangeLog new file mode 100644 index 000000000..97a8078c4 --- /dev/null +++ b/src/kadmin/export/ChangeLog @@ -0,0 +1,19 @@ +Thu Jul 18 20:39:32 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Mon Jul 15 16:51:51 1996 Marc Horowitz + + * export.c (print_princ): return should return a value. + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all + symbols to be resolved, so this needs to be here. + +Wed Jul 10 01:26:18 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 16:45:52 1996 Marc Horowitz + + * export.c: renamed to + diff --git a/src/kadmin/export/Makefile.in b/src/kadmin/export/Makefile.in new file mode 100644 index 000000000..5fa282d89 --- /dev/null +++ b/src/kadmin/export/Makefile.in @@ -0,0 +1,20 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE) + +PROG = kadm5_export +OBJS = ovsec_adm_export.o export.o export_err.o + +all:: $(PROG) + +export_err.c export_err.h: $(srcdir)/export_err.et + +export.o: export_err.h +ovsec_adm_export.o: export_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/export/Makefile.ov b/src/kadmin/export/Makefile.ov new file mode 100644 index 000000000..83e8c7219 --- /dev/null +++ b/src/kadmin/export/Makefile.ov @@ -0,0 +1,24 @@ +TOP = .. +include $(TOP)/config.mk/template +# CFLAGS := $(CFLAGS) -Wall + +# The next line *shouldn't* work, because the : should be a ::. +# However, it does work, and if I change it to :: gmake does really +# weird things. +ovsec_adm_export: export_err.h + +depend:: export_err.h + +PROG := kadm5_export +OBJS := ovsec_adm_export.o export.o export_err.o +SRCS := ovsec_adm_export.c export.c export_err.et +ETABLES := export_err.et + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDYN) $(LIBDB) + +expand ErrorTables +expand InstallAdmin +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/export/configure.in b/src/kadmin/export/configure.in new file mode 100644 index 000000000..992d591a1 --- /dev/null +++ b/src/kadmin/export/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_export.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/export/export.c b/src/kadmin/export/export.c new file mode 100644 index 000000000..3d41c4d9d --- /dev/null +++ b/src/kadmin/export/export.c @@ -0,0 +1,242 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include + +#include +#include "export_err.h" +#include "local.h" + +extern int errno; + +void print_key_data(FILE *f, krb5_key_data *key_data) +{ + int c; + + fprintf(f, "%d\t%d\t", key_data->key_data_type[0], + key_data->key_data_length[0]); + for(c = 0; c < key_data->key_data_length[0]; c++) + fprintf(f, "%02x ", + key_data->key_data_contents[0][c]); +} + +/* + * Function: print_princ + * + * Purpose: output osa_adb_princ_ent data in a human + * readable format (which is a format suitable for + * ovsec_adm_import consumption) + * + * Arguments: + * data (input) pointer to a structure containing a FILE * + * and a record counter. + * entry (input) entry to get dumped. + * void + * + * Requires: + * nuttin + * + * Effects: + * writes data to the specified file pointerp. + * + * Modifies: + * nuttin + * + */ +krb5_error_code print_princ(krb5_pointer data, krb5_db_entry *kdb) +{ + char *princstr; + int x, y, foundcrc, ret; + struct retdata *d; + krb5_tl_data tl_data; + osa_princ_ent_rec adb; + XDR xdrs; + + d = (struct retdata *) data; + + /* + * XXX Currently, lookup_tl_data always returns zero; it sets + * tl_data->tl_data_length to zero if the type isn't found. + * This should be fixed... + */ + /* + * XXX Should this function do nothing for a principal with no + * admin data, or print a record of "default" values? See + * comment in server_kdb.c to help decide. + */ + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + if ((ret = krb5_dbe_lookup_tl_data(d->context, kdb, &tl_data)) + || (tl_data.tl_data_length == 0)) + return(0); + + memset(&adb, 0, sizeof(adb)); + xdrmem_create(&xdrs, tl_data.tl_data_contents, + tl_data.tl_data_length, XDR_DECODE); + if (! xdr_osa_princ_ent_rec(&xdrs, &adb)) { + xdr_destroy(&xdrs); + return(OSA_ADB_XDR_FAILURE); + } + xdr_destroy(&xdrs); + + krb5_unparse_name(d->context, kdb->princ, &princstr); + fprintf(d->fp, "princ\t%s\t", princstr); + if(adb.policy == NULL) + fputc('\t', d->fp); + else + fprintf(d->fp, "%s\t", adb.policy); + fprintf(d->fp, "%x\t%d\t%d\t%d", adb.aux_attributes, + adb.old_key_len,adb.old_key_next, adb.admin_history_kvno); + + for (x = 0; x < adb.old_key_len; x++) { + if (! d->ovsec_compat) + fprintf(d->fp, "\t%d", adb.old_keys[x].n_key_data); + + foundcrc = 0; + for (y = 0; y < adb.old_keys[x].n_key_data; y++) { + krb5_key_data *key_data = &adb.old_keys[x].key_data[y]; + + if (d->ovsec_compat) { + if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC) + continue; + if (foundcrc) { + fprintf(stderr, error_message(EXPORT_DUP_DESCRC), + princstr); + continue; + } + foundcrc++; + } + fputc('\t', d->fp); + print_key_data(d->fp, key_data); + } + if (d->ovsec_compat && !foundcrc) + fprintf(stderr, error_message(EXPORT_NO_DESCRC), princstr); + } + + d->count++; + fputc('\n', d->fp); + free(princstr); + return(0); +} + +/* + * Function: print_policy + * + * Purpose: Print the contents of a policy entry in a human readable format. + * This format is also suitable for consumption for dbimport. + * + * Arguments: + * data (input) a pointer to a structure containing a FILE * + * and a record counter. + * entry (input) policy entry + * void + * + * Requires: + * nuttin + * + * Effects: + * writes data to file + * + * Modifies: + * nuttin + * + */ + +void +print_policy(void *data, osa_policy_ent_t entry) +{ + struct retdata *d; + + d = (struct retdata *) data; + fprintf(d->fp, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); + d->count++; + return; +} + +/* + * Function: export_principal + * + * Purpose: interates through the principal database with the + * osa_adb_iter_princ function which calls the print_princ + * routine with the FILE * of our filename. If the file + * name that gets passed in is NULL then we use stdout. + * + * Arguments: + * d (input) pointer to retdata. + * error code. 0 if sucsessful. + * + * Requires: + * nuttin + * + * Effects: + * calls osa_adb_iter_princ which calls print_princ + * + * Modifies: + * nuttin + * + */ +osa_adb_ret_t +export_principal(struct retdata *d, kadm5_config_params *params) +{ + int ret; + + if (ret = krb5_db_set_name(d->context, params->dbname)) + return ret; + + if (ret = krb5_db_init(d->context)) + return ret; + + if (ret = krb5_dbm_db_iterate(d->context, print_princ, d)) + return ret; + + if (ret = krb5_db_fini(d->context)) + return ret; + + return 0; +} + +/* + * Function: export_policy + * + * Purpose: iterates through the policy database with the + * osa_adb_iter_policy function which calls the print_policy + * routine with the FILE * of our filename. If the file name + * that gets passed in is NULL then we use stdout. + * + * Arguments: + * d (input) a pointer to retdata + * error code 0 if sucsessfull. + * + * Requires: + * nuttin + * + * Effects: + * calls osa_adb_iter_policy which calls print_policy + * + * Modifies: + * nuttin + * + */ +osa_adb_ret_t +export_policy(struct retdata *d, osa_adb_policy_t db) +{ + osa_adb_ret_t ret; + + if((ret = osa_adb_iter_policy(db, print_policy, (void *) d)) + != OSA_ADB_OK) { + return ret; + } + return OSA_ADB_OK; +} diff --git a/src/kadmin/export/export_err.et b/src/kadmin/export/export_err.et new file mode 100644 index 000000000..6c99a47b0 --- /dev/null +++ b/src/kadmin/export/export_err.et @@ -0,0 +1,19 @@ +error_table exp +error_code EXPORT_NO_ERR, "Database export complete, %d record%s processed.\n" +error_code EXPORT_UNK_OPTION, "Unknown Option\nUsage: ovsec_adm_export [filename]" +error_code EXPORT_OUTPUT_OPEN, "while opening output file" +error_code EXPORT_OUTPUT_CHMOD, "while changing mode of file" +error_code EXPORT_OUTPUT_STAT, "while trying to stat file" +error_code EXPORT_DATABASE_OPEN, "while opening database" +error_code EXPORT_PRINCIPAL, "while exporting principal database" +error_code EXPORT_POLICY, "while exporting policy database" +error_code EXPORT_LOCK, "while locking database" +error_code EXPORT_UNLOCK, "while unlocking database" +error_code EXPORT_CLOSE, "while closing database" +error_code EXPORT_SINGLE_RECORD, "" +error_code EXPORT_PLURAL_RECORDS, "s" +error_code EXPORT_NO_DESCRC, "Warning! No DES-CBC-CRC key for principal %s, cannot generate ovsec_adm_export-compatible record; skipping." +error_code EXPORT_DUP_DESCRC, "Warning! Multiple DES-CBC-CRC keys for principal %s; skipping duplicates." +error_code EXPORT_GET_CONFIG, "while retrieving configuration parameters" +end + diff --git a/src/kadmin/export/local.h b/src/kadmin/export/local.h new file mode 100644 index 000000000..3ec895ab2 --- /dev/null +++ b/src/kadmin/export/local.h @@ -0,0 +1,15 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +struct retdata { + krb5_context context; + FILE *fp; + int count; + int ovsec_compat; +}; + +osa_adb_ret_t export_principal(struct retdata *, kadm5_config_params *); +osa_adb_ret_t export_policy(struct retdata *d, osa_adb_policy_t); diff --git a/src/kadmin/export/ovsec_adm_export.c b/src/kadmin/export/ovsec_adm_export.c new file mode 100644 index 000000000..ded21ba55 --- /dev/null +++ b/src/kadmin/export/ovsec_adm_export.c @@ -0,0 +1,159 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "export_err.h" +#include "local.h" + +int +main(int argc, char *argv[]) +{ + char *filename; + struct retdata d; + struct stat statb; + int ret, fd; + time_t now; + char *whoami = argv[0]; + osa_adb_policy_t policy_db; + kadm5_config_params params; + + memset(¶ms, 0, sizeof(params)); + memset(&d, 0, sizeof(d)); + + filename = NULL; + initialize_exp_error_table(); + initialize_adb_error_table(); + krb5_init_context(&d.context); + krb5_init_ets(d.context); + + while(--argc) { + if(*++argv == NULL) + break; + if(!strcmp(*argv, "-princ")) { + params.dbname = *++argv; + params.mask |= KADM5_CONFIG_DBNAME; + continue; + } + if(!strcmp(*argv, "-policy")) { + params.admin_dbname = *++argv; + params.mask |= KADM5_CONFIG_ADBNAME; + continue; + } + if(!strcmp(*argv, "-ovsec")) { + d.ovsec_compat++; + continue; + } + if (*argv[0] == '-') { + com_err(whoami, EXPORT_UNK_OPTION, NULL); + exit(2); + } + if(filename == NULL) + filename = *argv; + else { + com_err(whoami, EXPORT_UNK_OPTION, NULL); + exit(2); + } + } + + if (ret = kadm5_get_config_params(d.context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(EXPORT_GET_CONFIG)); + exit(2); + } +#define REQUIRED_MASK (KADM5_CONFIG_DBNAME | \ + KADM5_CONFIG_ADBNAME) + if ((params.mask & REQUIRED_MASK) != REQUIRED_MASK) { + com_err(whoami, KADM5_BAD_SERVER_PARAMS, + error_message(EXPORT_GET_CONFIG)); + exit(2); + } + + if(filename != NULL) { + if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0400)) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_OPEN), filename); + exit(2); + } + if(fstat(fd, &statb) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_STAT), filename); + exit(2); + } + if(S_ISREG(statb.st_mode)) { + int mask = umask(0); + (void) umask(mask); + if (fchmod(fd, (0400 & ~mask)) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_CHMOD), filename); + exit(2); + } + } + if ((d.fp = fdopen(fd, "w")) == NULL) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_OPEN), filename); + exit(2); + } + } else d.fp = stdout; + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_DATABASE_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_SHARED) != OSA_ADB_OK)) { + com_err(argv[0], ret, error_message(EXPORT_LOCK)); + exit(2); + } + + d.count = 0; + + now = time(NULL); + if (d.ovsec_compat) + fprintf(d.fp, "OpenV*Secure V1.0\t%s", ctime(&now)); + else + fprintf(d.fp, "Kerberos KADM5 database V2.0\t%s", + ctime(&now)); + + if ((ret = export_policy(&d, policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, "%s (%s)", error_message(EXPORT_POLICY), + params.admin_dbname); + exit(2); + } + if ((ret = export_principal(&d, ¶ms)) != + OSA_ADB_OK) { + com_err(whoami, ret, "%s (%s)", error_message(EXPORT_PRINCIPAL), + params.dbname); + exit(2); + } + fprintf(d.fp, "End of Database\t%d\trecords\n", d.count); + + if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_UNLOCK)); + exit(2); + } + if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_CLOSE)); + exit(2); + } + + fprintf(stderr, error_message(EXPORT_NO_ERR), d.count, + (d.count == 1) ? error_message(EXPORT_SINGLE_RECORD) : + error_message(EXPORT_PLURAL_RECORDS)); + exit(0); +} + + + diff --git a/src/kadmin/export/unit-test/ChangeLog b/src/kadmin/export/unit-test/ChangeLog new file mode 100644 index 000000000..5db33c7c5 --- /dev/null +++ b/src/kadmin/export/unit-test/ChangeLog @@ -0,0 +1,5 @@ +Mon Jul 15 16:55:03 1996 Marc Horowitz + + * Makefile.ov (unit-test-body), dotest.sh: ovsec_adm_*port is now + kadm5_*port + diff --git a/src/kadmin/export/unit-test/Makefile.ov b/src/kadmin/export/unit-test/Makefile.ov new file mode 100644 index 000000000..25b1bf6c7 --- /dev/null +++ b/src/kadmin/export/unit-test/Makefile.ov @@ -0,0 +1,19 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(SAVE_FILES) + $(FIX_CONF_FILES) + $(INITDB) + +unit-test-body:: + $(RUNTEST) EXPORT=../kadm5_export --tool export + +unit-test-cleanup:: + $(RESTORE_FILES) diff --git a/src/kadmin/export/unit-test/add-to-db.sh b/src/kadmin/export/unit-test/add-to-db.sh new file mode 100644 index 000000000..c50541546 --- /dev/null +++ b/src/kadmin/export/unit-test/add-to-db.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +REALM=SECURE-TEST.OV.COM; export REALM +DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL + +$SRVTCL <<'EOF' +global r + +source $env(TCLUTIL) +set r $env(REALM) + +proc newpol { pname } { + puts stdout [ovsec_kadm_create_policy $server_handle [simple_policy "$pname"] {OVSEC_KADM_POLICY}] +} + +proc newprinc { name } { + global r + puts stdout [ovsec_kadm_create_principal $server_handle [simple_principal "$name@$r"] {OVSEC_KADM_PRINCIPAL} $name] +} + +proc chpass { princ pass } { + global server_handle + puts stdout [ovsec_kadm_chpass_principal $server_handle "$princ" "$pass"] +} + +puts stdout [ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle] + +puts stdout [ovsec_kadm_create_policy $server_handle "export_pwhist 0 0 0 0 10 0" {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM}] + +### Commented out since this isn't going to work for the december beta +#newprinc "export_with space" +#newprinc "export_with\"dquote" +#newprinc "export_with\nnewline" + +puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist1@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist1] + +chpass export_hist1@$r hist1_a + +puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist10@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist10] + +chpass export_hist10@$r hist10_a +chpass export_hist10@$r hist10_b +chpass export_hist10@$r hist10_c +chpass export_hist10@$r hist10_d +chpass export_hist10@$r hist10_e +chpass export_hist10@$r hist10_f +chpass export_hist10@$r hist10_g +chpass export_hist10@$r hist10_h +chpass export_hist10@$r hist10_i + +puts stdout [ovsec_kadm_destroy $server_handle] + +EOF diff --git a/src/kadmin/export/unit-test/config/unix.exp b/src/kadmin/export/unit-test/config/unix.exp new file mode 100644 index 000000000..e8d852f89 --- /dev/null +++ b/src/kadmin/export/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# export_version -- extract and print the version number of export +# + +proc export_version {} { + global EXPORT + set tmp [exec ident $EXPORT] + if [regexp {Header: .*export.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$EXPORT version $version\n" + } else { + clone_output "$EXPORT version \n" + } +} +# +# export_load -- loads the program +# +proc export_load {} { + # +} + +# export_exit -- clean up and exit +proc export_exit {} { + # +} + +# +# export_start -- start export running +# +proc export_start { args } { + global EXPORT + global spawn_id + + verbose "% $EXPORT $args" 1 + eval spawn $EXPORT $args +} diff --git a/src/kadmin/export/unit-test/dotest.sh b/src/kadmin/export/unit-test/dotest.sh new file mode 100644 index 000000000..53d4fe0ab --- /dev/null +++ b/src/kadmin/export/unit-test/dotest.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${BSDDB_DUMP=$TESTDIR/util/bsddb_dump} +DUMMY=${KDB5_EDIT=$TOP/../admin/edit/kdb5_edit} + +DPRINC=/tmp/dbdump.princ +DPOL=/tmp/dbdump.policy + +DPRINC1=$DPRINC.1 +DPRINC2=$DPRINC.2 + +DPOL1=$DPOL.1 +DPOL2=$DPOL.2 + +DEXPORT=/tmp/dbexport + +./add-to-db.sh + +rm -f $DEXPORT +../kadm5_export > $DEXPORT + +if $KDB5_EDIT -R ddb | sort > $DPRINC1; then + : +else + echo "error dumping princ.1" +fi +if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL1; then + : +else + echo "error dumping policy.1" +fi + +rm -f /krb5/kadb5* +touch /krb5/ovsec_adm.lock + +../../import/kadm5_import < $DEXPORT + +if $KDB5_EDIT -R ddb | sort > $DPRINC2; then + : +else + echo "error dumping princ.2" +fi +if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL2; then + : +else + echo "error dumping policy.2" +fi + + +status=0 + +if test -s $DPRINC1 && \ + test -s $DPRINC2 && \ + cmp -s $DPRINC1 $DPRINC2; then + echo "export/import principal db succeeded" +else + echo "export/import principal db failed" + status=1 +fi + +if test -s $DPOL1 && \ + test -s $DPOL2 && \ + cmp -s $DPOL1 $DPOL2; then + echo "export/import policy db succeeded" +else + echo "export/import policy db failed" + status=1 +fi + +if [ $status -eq 0 ]; then + rm -f $DPRINC* $DPOL* $DEXPORT +fi + +exit $status diff --git a/src/kadmin/export/unit-test/export.0/dotest.exp b/src/kadmin/export/unit-test/export.0/dotest.exp new file mode 100644 index 000000000..93ac21250 --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/dotest.exp @@ -0,0 +1,29 @@ +# +# $Id$ +# + +verbose "starting test: dotest.sh" + +spawn ./dotest.sh + +set timeout 60 + +expect { + -re "error dumping (princ|policy)\.(\[12\])" + { fail $expect_out(0,string); exp_continue } + -re "export/import (principal|policy) db (failed|succeeded)" + { + if {![string compare $expect_out(2,string) failed]} { + fail $expect_out(0,string) + } else { + pass $expect_out(0,string) + } + exp_continue + } + eof break + timeout { fail "timeout"; close } +} + +set w [wait] + +verbose "% Exit $w" diff --git a/src/kadmin/export/unit-test/export.0/output.exp b/src/kadmin/export/unit-test/export.0/output.exp new file mode 100644 index 000000000..6e0d4144b --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/output.exp @@ -0,0 +1,43 @@ +# +# $Id$ +# + +set timeout 30 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +exec rm -f /tmp/dbexport + +export_win "B.25: General success" /tmp/dbexport + +check_mode "B.26" /tmp/dbexport 0400 + +if {[catch "exec chmod 666 /tmp/dbexport" output]} { + unresolved "B.27: can't chmod /tmp/dbexport: $output" +} else { + export_win "prep for B.27" /tmp/dbexport + check_mode "B.27" /tmp/dbexport 0400 + exec rm -f /tmp/dbexport +} + +proc test28 {} { + if {[catch "file stat /dev/null stats" output]} { + unresolved "B.28: can't stat /dev/null: $output" + return + } + set stats(mode) [expr $stats(mode) & 07777] + if {$stats(mode) == [expr 0400]} { + if {[catch "exec chmod 666 /dev/null" output]} { + unresolved "B.28: can't chmod /dev/null: $output" + return + } + set stats(mode) [expr 0666] + } + export_win "prep for B.28" /dev/null + check_mode "B.28" /dev/null $stats(mode) +} +test28 diff --git a/src/kadmin/export/unit-test/export.0/usage.exp b/src/kadmin/export/unit-test/export.0/usage.exp new file mode 100644 index 000000000..9a592c9b8 --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/usage.exp @@ -0,0 +1,25 @@ +# +# $Id$ +# + +set timeout 30 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +export_lose "A.9: output file not writable" /foo/bar/baz \ + "No such file or directory while opening output file" + +export_lose "A.10: two arguments" {foo bar} \ + "Usage:" + +# XXX this depends on this being the last test run + +system {rm /krb5/kadb5} + +export_lose "A.2: /krb5 doesn't exist" /tmp/dbexport \ + "No such file or directory while opening database" + diff --git a/src/kadmin/export/unit-test/helpers.exp b/src/kadmin/export/unit-test/helpers.exp new file mode 100644 index 000000000..c53630f4b --- /dev/null +++ b/src/kadmin/export/unit-test/helpers.exp @@ -0,0 +1,126 @@ +# +# $Id$ +# + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "starting test: $name" + + set mytest_name "$name" + + eval export_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { ((![string compare $status zero]) && + ([lindex $ret $wait_status_index] == 0)) || + ((![string compare $status nonzero]) && + ([lindex $ret $wait_status_index] != 0)) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + +proc export_win { name args } { + mytest "$name" "$args" zero { + -re "Database export complete, \[0-9\]+ records processed." + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc export_lose { name args error } { + mytest "$name" "$args" nonzero { + -re "Database export complete, \[0-9\]+ records processed." + { close; myfail "unexpected success" } + -re "ovsec_adm_export: .*$error" + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc check_mode { test file mode } { + if {[catch "file stat $file stats" output]} { + unresolved "$test: can't stat $file: $output" + } else { + set stats(mode) [format "%o" [expr $stats(mode) & 07777]] + set mode [format "%o" [expr $mode]] + if {$stats(mode) != $mode} { + fail "$test: wrong mode ($stats(mode) should be $mode)" + } else { + verbose "$test: file $file has mode $mode" + pass $test + } + } +} diff --git a/src/kadmin/import/ChangeLog b/src/kadmin/import/ChangeLog new file mode 100644 index 000000000..ca7c0bc2d --- /dev/null +++ b/src/kadmin/import/ChangeLog @@ -0,0 +1,17 @@ +Thu Jul 18 20:40:32 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Mon Jul 15 16:58:08 1996 Marc Horowitz + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all + symbols to be resolved, so this needs to be here. + +Wed Jul 10 01:26:47 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 17:12:08 1996 Marc Horowitz + + * ovsec_adm_import.c, import.c: renamed to + diff --git a/src/kadmin/import/Makefile.in b/src/kadmin/import/Makefile.in new file mode 100644 index 000000000..2f15e706f --- /dev/null +++ b/src/kadmin/import/Makefile.in @@ -0,0 +1,18 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE) + +PROG = kadm5_import +OBJS = ovsec_adm_import.o import.o import_err.o strtok.o misc.o + +all:: $(PROG) + +import_err.c import_err.h: $(srcdir)/import_err.et +$(OBJS): import_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/import/Makefile.ov b/src/kadmin/import/Makefile.ov new file mode 100644 index 000000000..f2750313d --- /dev/null +++ b/src/kadmin/import/Makefile.ov @@ -0,0 +1,24 @@ +TOP = .. +include $(TOP)/config.mk/template +CFLAGS := $(CFLAGS) + +# The next line *shouldn't* work, because the : should be a ::. +# However, it does work, and if I change it to :: gmake does really +# weird things. +ovsec_adm_import: import_err.h + +depend:: import_err.h + +PROG := kadm5_import +OBJS := ovsec_adm_import.o import_err.o import.o strtok.o misc.o +SRCS := ovsec_adm_import.c import.c import_err.et strtok.c misc.c +ETABLES := import_err.et + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDYN) $(LIBDB) + +expand ErrorTables +expand InstallAdmin +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/import/configure.in b/src/kadmin/import/configure.in new file mode 100644 index 000000000..ad66ebd7c --- /dev/null +++ b/src/kadmin/import/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_import.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/import/import.c b/src/kadmin/import/import.c new file mode 100644 index 000000000..b1a8c0a76 --- /dev/null +++ b/src/kadmin/import/import.c @@ -0,0 +1,421 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include + +#include +#include "import_err.h" +#include "import.h" + +#define LINESIZE 32768 /* XXX */ +#define PLURAL(count) (((count) == 1) ? error_message(IMPORT_SINGLE_RECORD) : error_message(IMPORT_PLURAL_RECORDS)) + +int parse_pw_hist_ent(current, hist, ovsec_compat) + char *current; + osa_pw_hist_ent *hist; + int ovsec_compat; +{ + int tmp, i, j, ret; + char *cp; + + ret = 0; + if (!ovsec_compat) { + if ((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + return IMPORT_FAILED; + } + hist->n_key_data = atoi(cp); + } else + hist->n_key_data = 1; + + hist->key_data = (krb5_key_data *) malloc(hist->n_key_data * + sizeof(krb5_key_data)); + if (hist->key_data == NULL) + return ENOMEM; + memset(hist->key_data, 0, sizeof(krb5_key_data)*hist->n_key_data); + + for (i = 0; i < hist->n_key_data; i++) { + krb5_key_data *key_data = &hist->key_data[i]; + + key_data->key_data_ver = 1; + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_type[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_length[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + if(!(key_data->key_data_contents[0] = + (krb5_octet *) malloc(key_data->key_data_length[0]+1))) { + ret = ENOMEM; + goto done; + } + for(j = 0; j < key_data->key_data_length[0]; j++) { + if(sscanf(cp, "%02x", &tmp) != 1) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_contents[0][j] = tmp; + cp = strchr(cp, ' ') + 1; + } + } + +done: + return ret; +} + + + +/* + * Function: parse_principal + * + * Purpose: parse principal line in db dump file + * + * Arguments: + * 0 on sucsess, error code on failure + * + * Requires: + * principal database to be opened. + * nstrtok(3) to have a valid buffer in memory. + * + * Effects: + * [effects] + * + * Modifies: + * [modifies] + * + */ +int parse_principal(context, ovsec_compat) + krb5_context context; + int ovsec_compat; +{ + XDR xdrs; + osa_princ_ent_t rec; + osa_adb_ret_t ret; + krb5_tl_data tl_data; + krb5_principal princ; + krb5_db_entry kdb; + char *current; + char *cp; + int tmp, x, i, one, more; + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) + return IMPORT_BAD_FILE; + if((rec = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) + return ENOMEM; + memset(rec, 0, sizeof(osa_princ_ent_rec)); + if((ret = krb5_parse_name(context, cp, &princ))) + goto done; + krb5_unparse_name(context, princ, ¤t); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } else { + if(strcmp(cp, "")) { + if((rec->policy = (char *) malloc(strlen(cp)+1)) == NULL) { + ret = ENOMEM; + goto done; + } + strcpy(rec->policy, cp); + } else rec->policy = NULL; + } + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->aux_attributes = strtol(cp, (char **)NULL, 16); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_len = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_next = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->admin_history_kvno = atoi(cp); + if (! rec->old_key_len) { + rec->old_keys = NULL; + } else { + if(!(rec->old_keys = (osa_pw_hist_ent *) + malloc(sizeof(osa_pw_hist_ent) * rec->old_key_len))) { + ret = ENOMEM; + goto done; + } + memset(rec->old_keys,0, + sizeof(osa_pw_hist_ent) * rec->old_key_len); + for(x = 0; x < rec->old_key_len; x++) + parse_pw_hist_ent(current, &rec->old_keys[x], ovsec_compat); + } + + xdralloc_create(&xdrs, XDR_ENCODE); + if (! xdr_osa_princ_ent_rec(&xdrs, rec)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto done; + } + + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + tl_data.tl_data_length = xdr_getpos(&xdrs); + tl_data.tl_data_contents = xdralloc_getdata(&xdrs); + + one = 1; + ret = krb5_db_get_principal(context, princ, &kdb, &one, + &more); + if (ret) + goto done; + + if (ret = krb5_dbe_update_tl_data(context, &kdb, + &tl_data)) + goto done; + + if (ret = krb5_db_put_principal(context, &kdb, &one)) + goto done; + + xdr_destroy(&xdrs); + +done: + free(current); + krb5_free_principal(context, princ); + osa_free_princ_ent(rec); + return ret; +} + +/* + * Function: parse-policy + * + * Purpose: parse the ascii text of a dump file and turn it into + * a policy_ent_rec. + * + * Arguments: + * 0 on sucsess, error code on failure; + * + * Requires: + * nstrtok to have a buffer in memory + * + * Effects: + * write data out to db. + * + * Modifies: + * policy db. + * + */ +int +parse_policy(pol_db) + osa_adb_policy_t pol_db; +{ + osa_policy_ent_t rec; + char *cp; + osa_adb_ret_t ret; + + if((rec = (osa_policy_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) + return ENOMEM; + memset(rec, 0, sizeof(osa_princ_ent_rec)); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + ret = IMPORT_BAD_FILE; + goto done; + } + if((rec->name = (char *) malloc(strlen(cp) + 1)) == NULL) { + ret = ENOMEM; + goto done; + } + strcpy(rec->name, cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_life = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_max_life = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_length = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_classes = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_history_num = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->policy_refcnt = atoi(cp); + ret = osa_adb_create_policy(pol_db, rec); +done: + osa_free_policy_ent(rec); + return ret; +} + +/* + * Function: import-file + * + * Purpose: import a flat ascii file and convert it to a db file. + * + * Arguments: + * fp (input) file pointer to read db. + * 0 or error code on error. + * + * Requires: + * fp be valid + * + * Effects: + * calls appropriate routine to write out db files. + * + * Modifies: + * database file. + * + */ + +int import_file(krb5_context context, FILE *fp, int merge_princs, + osa_adb_policy_t pol_db) +{ + + int count = 0; + int errcnt = 0; + int ret = 0; + int found_footer = 0; + int file_count; + int ovsec_compat; + char line[LINESIZE]; + char version[BUFSIZ]; + char date[BUFSIZ]; + char *cp; + + if(fgets(line, LINESIZE, fp) == NULL) + return IMPORT_BAD_FILE; + if ((sscanf(line, "%[^\t]\t %[^\t]", version, date)) != 2) + return IMPORT_BAD_FILE; + if(!strcmp(version, VERSION_OVSEC_10)) + ovsec_compat++; + else if (strcmp(version, VERSION_KADM5_20)) + return IMPORT_BAD_VERSION; + + while(fgets(line, LINESIZE, fp) != (char *) NULL) { + if(found_footer) { + com_err(NULL, IMPORT_EXTRA_DATA, NULL); + break; + } + cp = nstrtok(line, "\t"); + if(!strcasecmp(cp, "princ")) { + if(merge_princs && + (ret = parse_principal(context, ovsec_compat)) != OSA_ADB_OK) { + if(ret == IMPORT_FAILED) { + if(!confirm()) + break; + else { + errcnt++; + continue; + } + } else break; + } else { + count++; + continue; + } + } + if(!strcasecmp(cp, "policy")) { + if((ret = parse_policy(pol_db)) != OSA_ADB_OK) { + if(ret == IMPORT_FAILED) { + if(!confirm()) + break; + else { + errcnt++; + continue; + } + } else break; + } else { + count++; + continue; + } + } + if(!strcasecmp(cp, "end of database")) { + found_footer = 1; + } else { + com_err(NULL, IMPORT_BAD_TOKEN, "%s", cp); + if(!confirm()) { + ret = IMPORT_BAD_FILE; + break; + } else { + errcnt++; + continue; + } + } + } + if(ret == OSA_ADB_OK && found_footer) { + if((cp = nstrtok(NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_FOOTER, NULL); + if(!confirm()) + ret = IMPORT_BAD_FOOTER; + else + ret = OSA_ADB_OK; + } else + file_count = atoi(cp); + if(file_count != (count + errcnt)) { + fprintf(stderr, error_message(IMPORT_COUNT_MESSAGE), file_count, + PLURAL(file_count), count, PLURAL(count)); + if(!confirm()) + ret = IMPORT_MISMATCH_COUNT; + else + ret = OSA_ADB_OK; + } else fprintf(stderr, error_message(IMPORT_NO_ERR), count, + PLURAL(count)); + } else if(ret == OSA_ADB_OK && !found_footer) { + com_err(NULL, IMPORT_BAD_FOOTER, NULL); + if(!confirm()) + ret = IMPORT_BAD_FOOTER; + else + ret = OSA_ADB_OK; + } + + return ret; +} + diff --git a/src/kadmin/import/import.h b/src/kadmin/import/import.h new file mode 100644 index 000000000..e0c3fedf9 --- /dev/null +++ b/src/kadmin/import/import.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.3 1996/07/22 20:26:27 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.2.4.1 1996/07/18 03:02:23 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.2.2.1 1996/06/20 21:48:24 marc + * File added to the repository on a branch + * + * Revision 1.2 1996/06/05 20:52:28 bjaspan + * initial hack at porting to mit kerberos + * + * Revision 1.1 1993/11/17 06:13:23 shanzer + * Initial revision + * + */ + +#include + +/* + * XXX These should be defined somewhere so import and export get the + * same value. + */ +#define VERSION_OVSEC_10 "OpenV*Secure V1.0" +#define VERSION_KADM5_20 "Kerberos KADM5 database V2.0" + +int import_file(krb5_context context, FILE *fp, int merge_princs, + osa_adb_policy_t pol_db); +int confirm(void); +char *nstrtok(char *str, char *delim); diff --git a/src/kadmin/import/import_err.et b/src/kadmin/import/import_err.et new file mode 100644 index 000000000..e091fe33c --- /dev/null +++ b/src/kadmin/import/import_err.et @@ -0,0 +1,26 @@ +error_table imp +error_code IMPORT_NO_ERR, "Successfully imported %d record%s.\n" +error_code IMPORT_BAD_FILE, "Input not recognized as database dump" +error_code IMPORT_BAD_TOKEN, "Bad token in dump file." +error_code IMPORT_BAD_VERSION, "Bad version in dump file" +error_code IMPORT_BAD_RECORD, "Defective record encountered: " +error_code IMPORT_BAD_FOOTER, "Truncated input file detected." +error_code IMPORT_FAILED, "Import of dump failed" +error_code IMPORT_COUNT_MESSAGE, "Mismatched record count: %d record%s indicated, %d record%s scanned.\n" +error_code IMPORT_MISMATCH_COUNT, "Number of records imported does not match count" +error_code IMPORT_UNK_OPTION, "Unknown command line option.\nUsage: ovsec_adm_import [filename]" +error_code IMPORT_WARN_DB, "Warning -- continuing to import will overwrite existing databases!" +error_code IMPORT_RENAME_FAILED, "Database rename Failed!!" +error_code IMPORT_EXTRA_DATA, "Extra data after footer is ignored." +error_code IMPORT_CONFIRM, "Proceed ?" +error_code IMPORT_OPEN_DUMP, "while opening input file" +error_code IMPORT_IMPORT, "while importing databases" +error_code IMPORT_TTY, "cannot open /dev/tty!!" +error_code IMPORT_RENAME_OPEN, "while opening databases" +error_code IMPORT_RENAME_LOCK, "while acquiring permanent lock" +error_code IMPORT_RENAME_UNLOCK, "while releasing permanent lock" +error_code IMPORT_RENAME_CLOSE, "while closing databases" +error_code IMPORT_SINGLE_RECORD, "" +error_code IMPORT_PLURAL_RECORDS, "s" +error_code IMPORT_GET_PARAMS, "while retrieving configuration parameters" +end diff --git a/src/kadmin/import/misc.c b/src/kadmin/import/misc.c new file mode 100644 index 000000000..bc58c7e7f --- /dev/null +++ b/src/kadmin/import/misc.c @@ -0,0 +1,95 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.4 1996/07/22 20:26:31 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.3.4.1 1996/07/18 03:02:26 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.3.2.1 1996/06/20 21:48:39 marc + * File added to the repository on a branch + * + * Revision 1.3 1994/04/11 23:52:10 jik + * Sandbox: + * + * Include to get the declaration of error_message. + * + * Revision 1.3 1994/03/29 21:18:54 jik + * Include to get the declaration of error_message. + * + * Revision 1.2 1993/12/21 18:59:25 shanzer + * make sure we prompt for input from /dev/tty + * + * Revision 1.1 1993/11/14 23:51:04 shanzer + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include /* for error_message() */ +#include "import_err.h" + +#ifndef TRUE +#define TRUE (1); +#endif +#ifndef FALSE +#define FALSE (0); +#endif + +/* + * Function: confirm + * + * Purpose: ask a yes or no question you must answer + * with a 'y|n|Y|n' + * + * Arguments: + * (input) none + * 1 if answered yes. 0 if no. + * + * Requires: + * IMPORT_CONFIRM be be defined. and com_err be init. + * + * Effects: + * none + * + * Modifies: + * nuttin + * + */ + +int +confirm(void) +{ + char buf[BUFSIZ]; /* can we say overkill ... */ + FILE *fp; + + if ((fp = fopen("/dev/tty", "r")) == NULL) { + fprintf(stderr, error_message(IMPORT_TTY)); + return FALSE; + } + while(1) { + fprintf(stderr, error_message(IMPORT_CONFIRM)); + fgets(buf, BUFSIZ, fp); + if(buf[0] == 'y' || buf[0] == 'Y') { + fclose(fp); + return TRUE; + } + if(buf[0] == 'n' || buf[0] == 'N') { + fclose(fp); + return FALSE; + } + } +} + diff --git a/src/kadmin/import/ovsec_adm_import.c b/src/kadmin/import/ovsec_adm_import.c new file mode 100644 index 000000000..4ca920434 --- /dev/null +++ b/src/kadmin/import/ovsec_adm_import.c @@ -0,0 +1,164 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include + +#include +#include "import_err.h" +#include "import.h" + +#define TMP_POLICY_FMT "/krb5/#ovsec_import_policy.%d" + +int +main(int argc, char *argv[]) +{ + char *filename, + *whoami; + int ret, merge_princs; + FILE *fp; + osa_adb_policy_t policy_db; + char pol_dbfile[BUFSIZ]; + kadm5_config_params params; + krb5_context context; + + filename = NULL; + initialize_imp_error_table(); + initialize_adb_error_table(); + krb5_init_context(&context); + krb5_init_ets(context); + + whoami = argv[0]; + merge_princs = 0; + while(--argc) { + if(*++argv == NULL) + break; + if (!strcmp(*argv, "-merge_princs")) { + merge_princs++; + continue; + } + if (*argv[0] == '-') { + com_err(whoami, IMPORT_UNK_OPTION, NULL); + exit(2); + } + if(filename == NULL) + filename = *argv; + else { + com_err(whoami, IMPORT_UNK_OPTION, NULL); + exit(2); + } + } + if(filename != NULL) { + if ((fp = fopen(filename, "r")) == NULL) { + com_err(whoami, errno, "%s (%s)", error_message(IMPORT_OPEN_DUMP), + filename); + exit(2); + } + } else fp = stdin; + + sprintf(pol_dbfile, TMP_POLICY_FMT, getpid()); + if(access(pol_dbfile, F_OK) == 0) { + if(unlink(pol_dbfile) != 0) + return errno; + } + + params.mask = 0; + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(IMPORT_GET_PARAMS)); + exit(2); + } +#define REQUIRED_MASK (KADM5_CONFIG_DBNAME | \ + KADM5_CONFIG_ADBNAME) + if ((params.mask & REQUIRED_MASK) != REQUIRED_MASK) { + com_err(whoami, KADM5_BAD_SERVER_PARAMS, + error_message(IMPORT_GET_PARAMS)); + exit(2); + } + /* + * This trick lets me use the temporary policy db name but the + * standard policy db lockfile, thus ensuring that no one changes + * the policy while this program is working. + */ + params.admin_dbname = pol_dbfile; + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) { + com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK)); + exit(2); + } + if (merge_princs) { + if ((ret = krb5_db_set_name(context, params.dbname)) || + (ret = krb5_db_init(context))) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + } + + if((ret = import_file(context, fp, merge_princs, policy_db)) != + OSA_ADB_OK) { + unlink(pol_dbfile); + com_err(whoami, ret, error_message(IMPORT_IMPORT)); + exit(2); + } + + if (merge_princs && (ret = krb5_db_fini(context))) { + com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE)); + exit(2); + } + + kadm5_free_config_params(context, ¶ms); + params.mask = 0; + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(IMPORT_GET_PARAMS)); + exit(2); + } + + if (access(params.admin_dbname, F_OK) == 0) { + puts(error_message(IMPORT_WARN_DB)); + if(!confirm()) { + com_err(whoami, IMPORT_FAILED, NULL); + exit(2); + } + } + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) { + com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK)); + exit(2); + } + if (rename(pol_dbfile, params.admin_dbname) != 0) { + com_err(whoami, IMPORT_RENAME_FAILED, NULL); + + /* WARNING! Permanent lock is not replaced. This will */ + /* require manual administrative action! */ + exit(2); + } + if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_UNLOCK)); + + /* WARNING! Permanent lock is not replaced. This will */ + /* require manual administrative action! */ + exit(2); + } + if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE)); + exit(2); + } + exit(0); +} diff --git a/src/kadmin/import/strtok.c b/src/kadmin/import/strtok.c new file mode 100644 index 000000000..7b7155786 --- /dev/null +++ b/src/kadmin/import/strtok.c @@ -0,0 +1,131 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.2 1996/07/22 20:26:35 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.1.4.1 1996/07/18 03:02:29 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.1.2.1 1996/06/20 21:49:01 marc + * File added to the repository on a branch + * + * Revision 1.1 1993/11/14 23:51:23 shanzer + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtok.c 5.7 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +/* + * Function: nstrtok + * + * Purpose: the same as strtok ... just different. does not deal with + * multiple tokens in row. + * + * Arguments: + * s (input) string to scan + * delim (input) list of delimiters + * string or null on error. + * + * Requires: + * nuttin + * + * Effects: + * sets last to string + * + * Modifies: + * last + * + */ + +char * +nstrtok(s, delim) + register char *s, *delim; +{ + register char *spanp; + register int c, sc; + char *tok; + static char *last; + + + if (s == NULL && (s = last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +#ifdef OLD +cont: + c = *s++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + last = NULL; + return (NULL); + } + tok = s - 1; +#else + tok = s; +#endif + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + diff --git a/src/kadmin/import/unit-test/Makefile.ov b/src/kadmin/import/unit-test/Makefile.ov new file mode 100644 index 000000000..c7ac33c0b --- /dev/null +++ b/src/kadmin/import/unit-test/Makefile.ov @@ -0,0 +1,9 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: + $(RUNTEST) IMPORT=../ovsec_adm_import --tool import diff --git a/src/kadmin/import/unit-test/config/unix.exp b/src/kadmin/import/unit-test/config/unix.exp new file mode 100644 index 000000000..af4ff443e --- /dev/null +++ b/src/kadmin/import/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# import_version -- extract and print the version number of import +# + +proc import_version {} { + global IMPORT + set tmp [exec ident $IMPORT] + if [regexp {Header: .*import.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$IMPORT version $version\n" + } else { + clone_output "$IMPORT version \n" + } +} +# +# import_load -- loads the program +# +proc import_load {} { + # +} + +# import_exit -- clean up and exit +proc import_exit {} { + # +} + +# +# import_start -- start import running +# +proc import_start { args } { + global IMPORT + global spawn_id + + verbose "% $IMPORT $args" 1 + eval spawn $IMPORT $args +} diff --git a/src/kadmin/import/unit-test/helpers.exp b/src/kadmin/import/unit-test/helpers.exp new file mode 100644 index 000000000..5905614e7 --- /dev/null +++ b/src/kadmin/import/unit-test/helpers.exp @@ -0,0 +1,93 @@ +# +# $Id$ +# + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + + verbose "starting test: $name" + + set mytest_name "$name" + + eval import_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret 0] == -1} { + fail "$name: wait returned error [lindex $ret 1]" + } else { + if { ((![string compare $status zero]) && + ([lindex $ret 1] == 0)) || + ((![string compare $status nonzero]) && + ([lindex $ret 1] != 0)) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret 1], should be $status" + } + } +} + +proc import_win { name args } { + mytest "$name" "$args" zero { + -re "Successfully imported \[0-9\]+ records." + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc import_lose { name args error } { + mytest "$name" "$args" nonzero { + -re "Successfully imported \[0-9\]+ records." + { myfail "unexpected success" } + -re "ovsec_adm_import: .*$error" + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + diff --git a/src/kadmin/import/unit-test/import.0/usage.exp b/src/kadmin/import/unit-test/import.0/usage.exp new file mode 100644 index 000000000..8e82f5a21 --- /dev/null +++ b/src/kadmin/import/unit-test/import.0/usage.exp @@ -0,0 +1,23 @@ +# +# $Id$ +# + +set timeout 5 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +import_lose "C.6: input file not readable" /foo/bar/baz \ + "No such file or directory while opening input file" + +import_lose "C.7: two arguments" {foo bar} \ + "Usage:" + +system {rm -rf /krb5} + +import_lose "C.2: /krb5 doesn't exist" ./valid_export_file \ + "Secure administration database lock file missing while importing" + diff --git a/src/kadmin/import/unit-test/valid_export_file b/src/kadmin/import/unit-test/valid_export_file new file mode 100644 index 000000000..dad19c874 --- /dev/null +++ b/src/kadmin/import/unit-test/valid_export_file @@ -0,0 +1,27 @@ +OpenV*Secure V1.0 Tue Dec 21 14:18:18 1993 +policy test-pol 0 10000 8 2 3 1 +policy dict-only 0 0 1 1 1 1 +policy once-a-min 30 0 1 1 1 1 +policy test-pol-nopw 0 0 1 1 1 2 +princ admin/delete@SECURE-TEST.OV.COM 0 0 0 0 +princ test3@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/modify@SECURE-TEST.OV.COM 0 0 0 0 +princ ovsec_adm/changepw@SECURE-TEST.OV.COM 0 0 0 0 +princ test2@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0 +princ admin/rename@SECURE-TEST.OV.COM 0 0 0 0 +princ test1@SECURE-TEST.OV.COM 0 0 0 0 +princ krbtgt/SECURE-TEST.OV.COM@SECURE-TEST.OV.COM 0 0 0 0 +princ pol3@SECURE-TEST.OV.COM dict-only 800 0 0 0 +princ admin/get-pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0 +princ admin/none@SECURE-TEST.OV.COM 0 0 0 0 +princ testuser@SECURE-TEST.OV.COM 0 0 0 0 +princ pol2@SECURE-TEST.OV.COM once-a-min 800 0 0 0 +princ K/M@SECURE-TEST.OV.COM 0 0 0 0 +princ pol1@SECURE-TEST.OV.COM test-pol 800 0 0 0 +princ ovsec_adm/history@SECURE-TEST.OV.COM 0 0 0 0 +princ admin@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/add@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/get@SECURE-TEST.OV.COM 0 0 0 0 +princ ovsec_adm/admin@SECURE-TEST.OV.COM 0 0 0 0 +End of Database 25 records diff --git a/src/kadmin/kdbkeys/ChangeLog b/src/kadmin/kdbkeys/ChangeLog new file mode 100644 index 000000000..206aea9be --- /dev/null +++ b/src/kadmin/kdbkeys/ChangeLog @@ -0,0 +1,16 @@ +Thu Jul 18 19:44:10 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Wed Jul 10 01:00:49 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support + + * kdbkeys.c: rename to , rename + + +Tue Jul 9 13:25:00 1996 Barry Jaspan + + * do-test.pl: rewrite to use kdb5_util instead of kdbkeys + + diff --git a/src/kadmin/kdbkeys/Makefile.in b/src/kadmin/kdbkeys/Makefile.in new file mode 100644 index 000000000..5672bd0bf --- /dev/null +++ b/src/kadmin/kdbkeys/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kdbkeys +OBJS = kdbkeys.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/kdbkeys/Makefile.ov b/src/kadmin/kdbkeys/Makefile.ov new file mode 100644 index 000000000..72c11695a --- /dev/null +++ b/src/kadmin/kdbkeys/Makefile.ov @@ -0,0 +1,21 @@ +# +# $Id$ +# + +TOP = .. +include $(TOP)/config.mk/template + +# Need $(STOP_SERVERS_LOCAL) because any running servers need to be +# killed so that they won't keep the database open so that we can't +# blow it away. + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS_LOCAL) + $(STOP_SERVERS_LOCAL) + +unit-test-body:: + $(PERL) ./do-test.pl 10 + +unit-test-cleanup:: diff --git a/src/kadmin/kdbkeys/configure.in b/src/kadmin/kdbkeys/configure.in new file mode 100644 index 000000000..6c027fc27 --- /dev/null +++ b/src/kadmin/kdbkeys/configure.in @@ -0,0 +1,11 @@ +AC_INIT(kdbkeys.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/kdbkeys/do-test.pl b/src/kadmin/kdbkeys/do-test.pl new file mode 100644 index 000000000..7acb425f7 --- /dev/null +++ b/src/kadmin/kdbkeys/do-test.pl @@ -0,0 +1,56 @@ +#!/afs/athena/contrib/perl/p + +# +# $Id$ +# + +$debug = $ARGV[1] || $ENV{'VERBOSE_TEST'}; + +die "Need a number.\n" if !$ARGV[0]; + +die "Neither \$TOP nor \$TESTDIR is set.\n" + if (! ($ENV{'TOP'} || $ENV{'TESTDIR'})); + +$TESTDIR = ($ENV{'TESTDIR'} || "$ENV{'TOP'}/testing"); +$INITDB = ($ENV{'INITDB'} || "$TESTDIR/scripts/init_db"); + +for ($i=0; $i<$ARGV[0]; $i++) { + print "Trial $i\n" if $debug; + + system("$INITDB > /dev/null 2>&1") && + die "Error in init_db\n"; + + open(KEYS,"../dbutil/kdb5_util -R dump_db|") || + die "Couldn't run kdb5_util: $!\n"; + chop($header = ); + if ($header ne "kdb5_util load_dump version 4") { + die "Cannot operate on dump version \"$header\"; version 4 required."; + } + while() { + next if ((!/^princ.*kadmin\//) && (!/^princ.*krbtgt/)); + + print if $debug > 1; + + split; + + $princ = $_[6]; + $nkeys = $_[4]; + $ntls = $_[3]; + print "$princ: nkeys $nkeys, ntls $ntls\n" if $debug; + for ($j = 15 + $ntls*3; $nkeys > 0; $nkeys--) { + $ver = $_[$j++]; + $kvno = $_[$j++]; + $keytype = $_[$j++]; + $keylen = $_[$j++]; + $keydata = $_[$j++]; + $j += 3 if ($ver > 1); + + print "$princ, ver $ver, kvno $kvno, type $keytype, len $keylen, " + . "data $keydata\n" if $debug; + + die "Duplicated key $princ = $keydata\n" if + $keys{$keydata}++; + } + } + close(KEYS); +} diff --git a/src/kadmin/keytab/ChangeLog b/src/kadmin/keytab/ChangeLog new file mode 100644 index 000000000..5aeb2a44e --- /dev/null +++ b/src/kadmin/keytab/ChangeLog @@ -0,0 +1,13 @@ +Thu Jul 18 20:41:14 1996 Marc Horowitz + + * keytab.c (etype_string): ifdef'd out reference to des3 + +Mon Jul 15 16:59:35 1996 Marc Horowitz + + * keytab.c (main): the default keytab name logic was inappropriate + for beta6, since it ignores the env vars, etc. This version still + has the problem that the default keytab probably isn't writeable. + +Wed Jul 10 01:27:44 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support diff --git a/src/kadmin/keytab/Makefile.in b/src/kadmin/keytab/Makefile.in new file mode 100644 index 000000000..74426a51f --- /dev/null +++ b/src/kadmin/keytab/Makefile.in @@ -0,0 +1,19 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadm5_keytab +OBJS = keytab.o + +all:: $(PROG).local $(PROG) + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/keytab/Makefile.ov b/src/kadmin/keytab/Makefile.ov new file mode 100644 index 000000000..15cd6d5d9 --- /dev/null +++ b/src/kadmin/keytab/Makefile.ov @@ -0,0 +1,30 @@ +# +# Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. +# +# $Id$ +# $Source$ +# + +TOP = .. +include $(TOP)/config.mk/template + +SRCS = keytab.c +OBJS = keytab.o +PROG = ovsec_edit_keytab + +LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \ + $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) \ + $(BSDLIB) $(NETLIB) + +expand InstallAdmin + +PROG = ovsec_edit_keytab.local +LIBS = $(LIBADMSRV) $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5) $(NDBMLIB) \ + $(LIBRPCLIB) $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBCOM_ERR) $(BSDLIB) $(NETLIB) + +expand InstallAdmin + +SUBDIRS = unit-test +expand SubdirTarget + diff --git a/src/kadmin/keytab/configure.in b/src/kadmin/keytab/configure.in new file mode 100644 index 000000000..0e043f7e5 --- /dev/null +++ b/src/kadmin/keytab/configure.in @@ -0,0 +1,12 @@ +AC_INIT(keytab.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/keytab/keytab.c b/src/kadmin/keytab/keytab.c new file mode 100644 index 000000000..2561252ef --- /dev/null +++ b/src/kadmin/keytab/keytab.c @@ -0,0 +1,528 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include + +#include +#include +#include + +int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str, + char *princ_str, int create); +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str); +static char *etype_string(krb5_enctype enctype); + +krb5_context context; +char *whoami; +int quiet; + +void usage() +{ + fprintf(stderr, "Usage: ovsec_edit_keytab [-k[eytab] keytab] [-q] cmd\n"); + fprintf(stderr, " cmds are:\t-a[dd] [-c[reate] [-p principal] principal\n"); + fprintf(stderr, "\t\t-c[hange] [-p principal] principal\n"); + fprintf(stderr, "\t\t-r[emove] principal [kvno|\"all\"|\"old\"]\n"); + exit(1); +} + +main(int argc0, char **argv0) +{ + extern krb5_kt_ops krb5_ktf_writable_ops; + krb5_keytab keytab = 0; + char *me_str, *princ_str, *keytab_str, *kvno_str; + char keytab_buf[1024]; + int argc, code, did_something, create; + char **argv; + + whoami = strrchr(argv0[0], '/') ? strrchr(argv0[0], '/') + 1 : argv0[0]; + + if (code = krb5_init_context(&context)) { + com_err(whoami, code, "while initializing krb5 context"); + exit(1); + } + + krb5_init_ets(context); + + /* register the WRFILE keytab type and set it as the default */ + if (code = krb5_kt_register(context, &krb5_ktf_writable_ops)) { + com_err(whoami, code, + "while registering writable key table functions"); + exit(1); + } + + /* process non-action arguments first */ + argc = argc0-1; + argv = argv0+1; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc) usage(); + + if (keytab == NULL) { + if (strchr(*argv, ':') != NULL) { + keytab_str = strdup(*argv); + if (keytab_str == NULL) { + com_err(whoami, ENOMEM, + "while creating keytab name"); + exit(1); + } + } else { + keytab_str = (char *) + malloc(strlen("WRFILE:")+strlen(*argv)+1); + if (keytab_str == NULL) { + com_err(whoami, ENOMEM, + "while creating keytab name"); + exit(1); + } + sprintf(keytab_str, "WRFILE:%s", *argv); + } + + code = krb5_kt_resolve(context, keytab_str, &keytab); + if (code != 0) { + com_err(whoami, code, "while resolving keytab %s", + keytab_str); + exit(1); + } + } else { + usage(); + } + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } + /* otherwise ignore the argument, for now */ + argc--; argv++; + } + + if (keytab == NULL) { + code = krb5_kt_default(context, &keytab); + if (code != 0) { + com_err(whoami, code, "while opening default keytab"); + exit(1); + } + code = krb5_kt_get_name(context, keytab, + keytab_buf, sizeof(keytab_buf)); + keytab_str = keytab_buf; + } + + argc = argc0-1; + argv = argv0+1; + + did_something = 0; + + /* now process the action arguments */ + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + /* if there is no keytab argument the previous loop */ + /* would have called usage(), so just skip it */ + argc--; argv++; + } else if (strcmp(*argv, "-q") == 0) { + /* skip it */ + } else if (strncmp(*argv, "-a", 2) == 0 || + strncmp(*argv, "-c", 2) == 0) { + did_something++; + + argc--; argv++; + if (!argc) usage(); + + me_str = NULL; + create = 0; + while (argc) { + if (strcmp(*argv, "-p") == 0) { + argc--; argv++; + if (argc < 1) usage(); + + me_str = *argv; + } else if (strncmp(*argv, "-c", 2) == 0) { + create++; + } else + break; + argc--; argv++; + } + if (argc != 1) usage(); + + code = add_principal(keytab_str, keytab, me_str ? me_str : + *argv, *argv, create); + break; + } else if (strncmp(*argv, "-r", 2) == 0) { + did_something++; + + argc--; argv++; + if (!argc) usage(); + princ_str = *argv; + if (argc > 0) { + argc--; + argv++; + kvno_str = *argv; + } else + kvno_str = NULL; + + code = remove_principal(keytab_str, keytab, princ_str, + kvno_str); + break; + } else { + fprintf(stderr, "%s: Unknown command line option %s.\n", + whoami, *argv); + usage(); + } + + argc--; argv++; + } + + /* argv ends up pointing at the last recognized argument */ + if (!did_something || argc > 1) + usage(); + + /* use argc as temp */ + argc = krb5_kt_close(context, keytab); + if (argc != 0) { + com_err(whoami, argc, "while closing keytab"); + code = argc; + } + + free(keytab_str); + + return (code != 0); +} + +int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str, + char *princ_str, int create) +{ + kadm5_principal_ent_rec princ_rec; + krb5_principal me, princ; + krb5_keytab_entry new_entry; + krb5_keyblock *keys; + void *handle; + int code, code2, mask, nkeys, i; + + (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); + + me = princ = NULL; + handle = NULL; + keys = NULL; + nkeys = 0; + + code = krb5_parse_name(context, me_str, &me); + if (code != 0) { + com_err(whoami, code, "while parsing -p principal name %s", + me_str); + goto cleanup; + } + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing -add principal name %s", + princ_str); + goto cleanup; + } + + /* first try using the keytab */ + code = kadm5_init_with_skey(me_str, keytab_str, + KADM5_ADMIN_SERVICE, + NULL, /* default configuration */ + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, &handle); + if (code != 0) { + /* KRB5_KT_NOTFOUND and ENOENT are not "errors" because this */ + /* program does not require the keytab entry to exist */ + if (code != KRB5_KT_NOTFOUND && code != ENOENT) { + if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, me_str); + else + com_err(whoami, code, "while authenticating as principal " + "%s from keytab", me_str); + } + + code2 = kadm5_init_with_password(me_str, NULL, + KADM5_ADMIN_SERVICE, + NULL, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + if (code2 != 0) { + if (code2 != code) /* don't dup error messages */ { + com_err(whoami, code2, "while authenticating as " + "principal %s from password", me_str); + } + goto cleanup; + } + } + + if (create) { + /* always try to create and just ignore dup errors because it */ + /* reduces duplicate code... and how often will this happen? */ + + /* be sure to create the principal with the secure sequence */ + /* of events as specified in the functional spec */ + + princ_rec.principal = princ; + princ_rec.attributes = KRB5_KDB_DISALLOW_ALL_TIX; + mask = KADM5_PRINCIPAL | KADM5_ATTRIBUTES; + code = kadm5_create_principal(handle, &princ_rec, + mask, "dummy"); + if (code == KADM5_DUP) { + printf("%s: Principal %s already exists.\n", + whoami, princ_str); + } else if (code != 0) { + if (code == KADM5_AUTH_ADD) { + fprintf(stderr, "%s: Operation requires " + "``add'' and ``modify'' privileges while creating " + "principal.\n", whoami); + } else { + com_err(whoami, code, "while creating " + "principal %s.", princ_str); + } + goto cleanup; + } else if (!quiet) + printf("%s: Created principal %s.\n", whoami, princ_str); + } + + code = kadm5_randkey_principal(handle, princ, &keys, &nkeys); + if (code != 0) { + if (code == KADM5_UNK_PRINC) { + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, princ_str); + } else + com_err(whoami, code, "while changing %s's key", + princ_str); + goto cleanup; + } + + code = kadm5_get_principal(handle, princ, &princ_rec, + KADM5_PRINCIPAL_NORMAL_MASK); + if (code != 0) { + com_err(whoami, code, "while retrieving principal"); + goto cleanup; + } + + if (create) { + /* complete the secure principal-creation sequence */ + princ_rec.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; + mask = KADM5_ATTRIBUTES; + code = kadm5_modify_principal(handle, &princ_rec, mask); + if (code != 0) { + if (code == KADM5_AUTH_ADD) { + fprintf(stderr, "%s: Operation requires " + "``add'' and ``modify'' privileges while creating " + "principal.\n", whoami); + } else + com_err(whoami, code, "while modifying newly created " + "principal"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + } + + for (i = 0; i < nkeys; i++) { + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = princ; + new_entry.key = keys[i]; + new_entry.vno = princ_rec.kvno; + + code = krb5_kt_add_entry(context, keytab, &new_entry); + if (code != 0) { + com_err(whoami, code, "while adding key to keytab"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + + if (!quiet) + printf("%s: Entry for principal %s with kvno %d, " + "encryption type %s added to keytab %s.\n", + whoami, princ_str, princ_rec.kvno, + etype_string(keys[i].enctype), keytab_str); + } + + code = kadm5_free_principal_ent(handle, &princ_rec); + if (code != 0) { + com_err(whoami, code, "while freeing principal entry"); + goto cleanup; + } + +cleanup: + if (handle) { + code2 = kadm5_destroy(handle); + if (code2 != 0) { + com_err(whoami, code2, "while closing admin server connection"); + } + } + if (nkeys) { + for (i = 0; i < nkeys; i++) + krb5_free_keyblock(context, &keys[i]); + free(keys); + } + if (me) + krb5_free_principal(context, me); + if (princ) + krb5_free_principal(context, princ); + + return code; +} + +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str) +{ + krb5_principal princ; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; + int code, kvno, did_something; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing principal name %s", + princ_str); + return code; + } + + mode = UNDEF; + if (kvno_str == NULL) { + mode = HIGH; + kvno = 0; + } else if (strcmp(kvno_str, "all") == 0) { + mode = ALL; + kvno = 0; + } else if (strcmp(kvno_str, "old") == 0) { + mode = OLD; + kvno = 0; + } else { + mode = SPEC; + kvno = atoi(kvno_str); + } + + /* kvno is set to specified value for SPEC, 0 otherwise */ + code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); + if (code != 0) { + if (code == ENOENT) { + fprintf(stderr, "%s: Keytab %s does not exist.\n", + whoami, keytab_str); + } else if (code == KRB5_KT_NOTFOUND) { + if (mode != SPEC) + fprintf(stderr, "%s: No entry for principal " + "%s exists in keytab %s\n", + whoami, princ_str, keytab_str); + else + fprintf(stderr, "%s: No entry for principal " + "%s with kvno %d exists in keytab " + "%s.\n", whoami, princ_str, kvno, + keytab_str); + } else { + com_err(whoami, code, "while retrieving highest kvno " + "from keytab"); + } + return code; + } + + /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ + kvno = entry.vno; + krb5_kt_free_entry(context, &entry); + + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while starting keytab scan"); + return code; + } + + did_something = 0; + while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { + if (krb5_principal_compare(context, princ, entry.principal) && + ((mode == ALL) || + (mode == SPEC && entry.vno == kvno) || + (mode == OLD && entry.vno != kvno) || + (mode == HIGH && entry.vno == kvno))) { + + /* + * Ack! What a kludge... the scanning functions lock + * the keytab so entries cannot be removed while they + * are operating. + */ + code = krb5_kt_end_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while temporarily ending " + "keytab scan"); + return code; + } + code = krb5_kt_remove_entry(context, keytab, &entry); + if (code != 0) { + com_err(whoami, code, "while deleting entry from keytab"); + return code; + } + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while restarting keytab scan"); + return code; + } + + did_something++; + if (!quiet) + printf("%s: Entry for principal %s with kvno %d " + "removed from keytab %s.\n", whoami, + princ_str, entry.vno, keytab_str); + } + krb5_kt_free_entry(context, &entry); + } + if (code && code != KRB5_KT_END) { + com_err(whoami, code, "while scanning keytab"); + return code; + } + if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) { + com_err(whoami, code, "while ending keytab scan"); + return code; + } + + /* + * If !did_someting then mode must be OLD or we would have + * already returned with an error. But check it anyway just to + * prevent unexpected error messages... + */ + if (!did_something && mode == OLD) { + fprintf(stderr, "%s: There is only one entry for principal " + "%s in keytab %s\n", whoami, princ_str, keytab_str); + return 1; + } + + return 0; +} + +/* + * etype_string(enctype): return a string representation of the + * encryption type. XXX copied from klist.c; this should be a + * library function, or perhaps just #defines + */ +static char *etype_string(enctype) + krb5_enctype enctype; +{ + static char buf[12]; + + switch (enctype) { + case ENCTYPE_DES_CBC_CRC: + return "DES-CBC-CRC"; + break; + case ENCTYPE_DES_CBC_MD4: + return "DES-CBC-MD4"; + break; + case ENCTYPE_DES_CBC_MD5: + return "DES-CBC-MD5"; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + return "DES3-CBC-MD5"; + break; +#endif + default: + sprintf(buf, "etype %d", enctype); + return buf; + break; + } +} diff --git a/src/kadmin/keytab/unit-test/ChangeLog b/src/kadmin/keytab/unit-test/ChangeLog new file mode 100644 index 000000000..69df6be92 --- /dev/null +++ b/src/kadmin/keytab/unit-test/ChangeLog @@ -0,0 +1,4 @@ +Mon Jul 15 17:03:28 1996 Marc Horowitz + + * Makefile.ov (unit-test-body): ovsec_adm_keytab is now + kadm5_keytab diff --git a/src/kadmin/keytab/unit-test/Makefile.ov b/src/kadmin/keytab/unit-test/Makefile.ov new file mode 100644 index 000000000..1b3366d8b --- /dev/null +++ b/src/kadmin/keytab/unit-test/Makefile.ov @@ -0,0 +1,21 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-body:: + $(CLNTTCL) ./del-princs.tcl + $(RUNTEST) KEYTAB=../kadm5_keytab \ + KLIST=../../../clients/klist/klist \ + QUALNAME=../../testing/scripts/qualname --tool keytab + +unit-test-setup:: + $(START_SERVERS) + $(CLNTTCL) ./add-princs.tcl + +unit-test-cleanup:: + $(STOP_SERVERS) diff --git a/src/kadmin/keytab/unit-test/add-princs.tcl b/src/kadmin/keytab/unit-test/add-princs.tcl new file mode 100644 index 000000000..247a4382b --- /dev/null +++ b/src/kadmin/keytab/unit-test/add-princs.tcl @@ -0,0 +1,12 @@ +source $env(TCLUTIL) + +ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle + +ovsec_kadm_create_principal $server_handle [simple_principal kttest1] \ + {OVSEC_KADM_PRINCIPAL} kttest1 + +ovsec_kadm_create_principal $server_handle [simple_principal kttest2] \ + {OVSEC_KADM_PRINCIPAL} kttest2 + +ovsec_kadm_destroy $server_handle diff --git a/src/kadmin/keytab/unit-test/config/unix.exp b/src/kadmin/keytab/unit-test/config/unix.exp new file mode 100644 index 000000000..bc07d4f6e --- /dev/null +++ b/src/kadmin/keytab/unit-test/config/unix.exp @@ -0,0 +1,46 @@ +set klist $KLIST +set hostname hostname +set qualname $QUALNAME + +set rm /bin/rm + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc keytab_version {} { + global KEYTAB + puts "$KEYTAB version unknown" +} + +proc keytab_load {} { + # +} + +proc keytab_exit {} { + # +} + +proc keytab_start { args } { + global KEYTAB + global spawn_id + + verbose "% $KEYTAB $args" 1 + eval spawn $KEYTAB $args +} + + diff --git a/src/kadmin/keytab/unit-test/del-princs.tcl b/src/kadmin/keytab/unit-test/del-princs.tcl new file mode 100644 index 000000000..9b9cab5c4 --- /dev/null +++ b/src/kadmin/keytab/unit-test/del-princs.tcl @@ -0,0 +1,24 @@ +source $env(TCLUTIL) + +proc check_err {error} { + if {! [string match {*OVSEC_KADM_UNK_PRINC*} $error]} { + error $error + } +} + +proc delprinc {princ} { + global server_handle + + catch {ovsec_kadm_delete_principal $server_handle $princ} + if {[info exists errorInfo]} { + check_err $errorInfo + } +} + +ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle + +delprinc dne1 +delprinc dne2 + +ovsec_kadm_destroy $server_handle diff --git a/src/kadmin/keytab/unit-test/helpers.exp b/src/kadmin/keytab/unit-test/helpers.exp new file mode 100644 index 000000000..a9f7ca402 --- /dev/null +++ b/src/kadmin/keytab/unit-test/helpers.exp @@ -0,0 +1,132 @@ +# +# $Id$ +# + +# +# Create a keytab "name" with an entry for each element in the array +# "entries". If "name" already exists it is destroyed. Connections +# to the admin server are made as the principal "admin" with the +# password "password". +# +proc setup_keytab { testname ktname admin password entries } { + global klist rm + global wait_error_index wait_status_index + global verbose + + verbose "setting up test: $testname" 1 + + if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} { + set filename $ktname + } + + if {[file exists $filename] && [catch "exec $rm $filename"] != 0} { + error "$testname: cannot delete keytab file $filename"; + } + + if {$type == "WRFILE"} { + set type "FILE" + } + + foreach entry $entries { + keytab_run "$testname setup" \ + "-k $ktname -a -p $admin $entry" 0 { + "Enter password:" { + send "$password\n" + } + } + # if "Enter password:" needs to be optional: + # { timeout { } } + } + + if {$verbose > 1} { + if {[file exists $filename]} { + puts "% exec $klist -k $type:$filename\n" + if {[catch "exec $klist -k $type:$filename"] != 0} { + error "$testname: $klist failed" + } + } + } +} + +# +# Run $KEYTAB with args ktargs. Each element of args is treated as an +# expect block for the process, in turn. If all elements match and +# then eof occurs with exit status status, the test passes; otherwise +# it fails. +# +proc keytab_run { testname ktargs status args } { + global spawn_id timeout + global wait_error_index wait_status_index + global progname + + verbose "running $progname for test: $testname" 2 + + eval keytab_start $ktargs + + # wait for eof after exps + lappend args { eof { verbose $expect_out(buffer) 2 } } + + foreach exp $args { + uplevel 1 "expect { + $exp + timeout { close; fail \"$testname: timeout\"; return } + eof { fail \"$testname: eof before expected message\"; return } + }" + } + + set ret [wait] + verbose "% Exit $ret" 2 + + if {[lindex $ret $wait_error_index] == -1} { + fail "$testname: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$testname" + } else { + fail "$testname: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + + +proc klist_check { testname ktname args } { + global klist + + if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} { + set filename $ktname + } + + set lines [list "^Keytab name: (WR)?FILE:$filename" \ + "^KVNO Principal" "^---- -------"] + + foreach entry $args { + if {[lindex $entry 1] == 0} { + set line "^ *\[0-9\]+ [lindex $entry 0]" + } else { + set line "^ *[lindex $entry 1] [lindex $entry 0]" + } + lappend lines $line + } + + set kl [open "|$klist -k FILE:$filename" r] + + while {[gets $kl line] >= 0} { + if {([llength $lines] == 0) || + ([regexp [lindex $lines 0] $line] == 0)} { + fail "$testname: klist check: \ + [lindex $lines 0] does not match $line" + } + set lines [lrange $lines 1 end] + } + if {[catch "close $kl" msg] != 0} { + fail "$testname: klist: $msg" + return + } + + if {[llength $lines] == 0} { + pass "$testname: klist check" + } else { + fail "$testname: klist check: too few entries in keytab" + } +} diff --git a/src/kadmin/keytab/unit-test/keytab.0/ChangeLog b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog new file mode 100644 index 000000000..1b807089a --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog @@ -0,0 +1,4 @@ +Mon Jul 15 17:09:01 1996 Marc Horowitz + + * keytab-spec.exp: use /krb5/v5srvtab, since /krb5 is the test + dir. diff --git a/src/kadmin/keytab/unit-test/keytab.0/adding.exp b/src/kadmin/keytab/unit-test/keytab.0/adding.exp new file mode 100644 index 000000000..159ac638b --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/adding.exp @@ -0,0 +1,119 @@ +# +# $Id$ +# + +set timeout 20 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set ktscratch_file /tmp/keytab_test +set ktscratch WRFILE:$ktscratch_file +set ktarg "-k $ktscratch" +set add_admin "$ktarg -a -p admin" +set pwprompt { "Enter password:" { send "admin\n" } } + +setup_keytab "A1,A6" $ktscratch admin admin {} +keytab_run "A1,A6" "$add_admin kttest1" 0 "$pwprompt" { + -re \ + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A1,A6" $ktscratch {kttest1 0} + +setup_keytab "A2" $ktscratch admin admin {} +keytab_run "A2" "-q $add_admin kttest1" 0 "$pwprompt" { + -re + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." { + close; fail "A2: -q"; return } + eof { break } +} +klist_check "A2" $ktscratch {kttest1 0} + +setup_keytab "A3" $ktscratch admin admin {kttest1 kttest1} +set kvno_key1 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ && NR==4 {print \$1 \" \" \$3}"] +set kvno_key2 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ && NR==5 {print \$1 \" \" \$3}"] +if {[lindex $kvno_key1 1] == [lindex $kvno_key2 1]} { + fail "A3: key compare" +} else { + klist_check "A3" $ktscratch "kttest1 [lindex $kvno_key1 0]" \ + "kttest1 [expr [lindex $kvno_key1 0]+1]" +} + +setup_keytab "A7" $ktscratch admin admin {} +keytab_run "A7" "$add_admin does-not-exist" 1 "$pwprompt" { + -re + "$progname: Principal does-not-exist does not exist." {} +} + +setup_keytab "A4,A10,A11,A13,A15" $ktscratch admin admin { kttest1 kttest2} +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0 +klist_check "A4,A10,A11,A13,A15" $ktscratch { kttest1 0 } \ + { kttest2 0 } { kttest1 0 } { kttest2 0 } { kttest1 0 } { kttest2 0 } + +setup_keytab "A12" $ktscratch admin admin {} +keytab_run "A12" "$ktarg -a -p admin/get-add kttest1" 1 "$pwprompt" { + "Operation requires ``change-password'' privilege while changing" {} +} + +setup_keytab "A14" $ktscratch admin admin {} +# assume the exit status won't be -1, so if the password prompt +# doesn't appear the test will fail +keytab_run "A14" "$ktarg -a kttest1" -1 { + "Enter password:" { send "\n"; expect eof; pass "A14: no -p"; return } +} + +setup_keytab "A16" $ktscratch admin admin {} +keytab_run "A16" "$ktarg -a -p does-not-exist kttest1" 1 { + "$progname: Principal does-not-exist does not exist." {} +} + +setup_keytab "A17" $ktscratch admin admin { kttest1 kttest2} +keytab_run "A17" "$ktarg -a -p kttest2 kttest1" 1 { + "Enter password:" { close; fail "A17: no password prompt"; return } + default { break } +} + +setup_keytab "A18" $ktscratch admin admin { } +keytab_run "A18" "$ktarg -a -c -p admin dne1" 0 "$pwprompt" { + "$progname: Created principal dne1" {} +} { + -re + "$progname: Entry.*dne1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A18" $ktscratch {dne1 0} + +setup_keytab "A19" $ktscratch admin admin {} +keytab_run "A9" "-q $ktarg -a -c -p admin dne2" 0 "$pwprompt" { + "$progname: Created principal dne2" { close; fail "A19: -q"; return } + eof { break } +} +klist_check "A19" $ktscratch {dne2 0} + +setup_keytab "A21" $ktscratch admin admin {} +keytab_run "A21" "$ktarg -a -c -p admin kttest1" 0 "$pwprompt" { + "$progname: Principal kttest1 already exists." {} +} { + -re \ + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A21" $ktscratch {kttest1 0} + +setup_keytab "A22" $ktscratch admin admin {} +keytab_run "A22" "$ktarg -a -c -p admin/modify kttest1" 1 "$pwprompt" { + "Operation requires ``add'' and ``modify'' privileges while creating" {} +} + +setup_keytab "A23" $ktscratch admin admin {} +keytab_run "A23" "$ktarg -a -c -p admin/get kttest1" 1 "$pwprompt" { + "Operation requires ``add'' and ``modify'' privileges while creating" {} +} + +exec rm -f $ktscratch_file diff --git a/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp new file mode 100644 index 000000000..8da80eb69 --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp @@ -0,0 +1,47 @@ +# +# $Id$ +# + +set timeout 10 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set hname [exec $hostname] +set qname [exec $qualname $hname] + +set testfile1 /tmp/keytab-test1 +set testfile2 /tmp/keytab-test2 + +if {[info exists env(KRB5_KTNAME)]} { + set ktname_orig $env(KRB5_KTNAME) + unset env(KRB5_KTNAME) +} + +setup_keytab "K1" WRFILE:/krb5/v5srvtab admin admin "host/$qname" +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" +keytab_run "K1" "-a host/$qname" 0 +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" "host/$qname 0" +keytab_run "K1" "-r host/$qname old" 0 +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" + +if {[info exists ktname_orig]} { + set env(KRB5_KTNAME) $ktname_orig +} + +setup_keytab "K2" WRFILE:$testfile1 admin admin {} +keytab_run "K2" "-k WRFILE:$testfile1 -a -p admin kttest1" 0 { + "Enter password:" { send "admin\n" } +} +klist_check "K2" FILE:$testfile1 "kttest1 0" + +setup_keytab "K2" WRFILE:$testfile2 admin admin {} +keytab_run "K3" "-k $testfile2 -a -p admin kttest1" 0 { + "Enter password:" { send "admin\n" } +} +klist_check "K3" FILE:$testfile2 "kttest1 0" + +exec rm -f $testfile1 $testfile2 diff --git a/src/kadmin/keytab/unit-test/keytab.0/removing.exp b/src/kadmin/keytab/unit-test/keytab.0/removing.exp new file mode 100644 index 000000000..a7a50f045 --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/removing.exp @@ -0,0 +1,125 @@ +# +# $Id$ +# + +set timeout 10 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set ktscratch_file /tmp/keytab_test +set ktscratch WRFILE:$ktscratch_file +set ktarg "-k $ktscratch" + +# Get the kvnos we will need later +setup_keytab "setup" $ktscratch admin admin { kttest1 kttest2 } +set kvno1 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ {print \$1}"] +set kvno2 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest2/ {print \$1}"] + +setup_keytab "R1" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R1" "$ktarg -r kttest1" 0 { + -re + "$progname: Entry for principal kttest1 with kvno \[0-9\]+\ + removed from keytab $ktscratch" {} +} +klist_check "R1" $ktscratch + +setup_keytab "R2" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R2" "$ktarg -q -r kttest1" 0 { + -re + "$progname: Entry for principal kttest1 with kvno \[0-9\]+\ + removed from keytab $ktscratch" { close; fail "R2: -q"; return } + eof { break } +} +klist_check "R2" $ktscratch + +setup_keytab "R3" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +klist_check "R3" $ktscratch "kttest1 $kvno1" +keytab_run "R3" "$ktarg -r kttest1 $kvno1" 0 +klist_check "R3" $ktscratch + +setup_keytab "R4" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \ + "kttest1 [expr $kvno1-1]" "kttest1 $kvno1" +keytab_run "R4" "$ktarg -r kttest1" 0 +klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \ + "kttest1 [expr $kvno1-1]" + +setup_keytab "R5" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R5" "$ktarg -r kttest1 old" 0 +klist_check "R5" $ktscratch "kttest1 $kvno1" + +setup_keytab "R6" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R6" "$ktarg -r kttest1 old" 0 +klist_check "R6" $ktscratch "kttest1 $kvno1" + +setup_keytab "R7" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R7" "$ktarg -r kttest1 all" 0 { + "$progname: Entry for principal kttest1" {} +} { + "$progname: Entry for principal kttest1" {} +} { + "$progname: Entry for principal kttest1" {} +} +klist_check "R7" $ktscratch + +setup_keytab "R8" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R8" "$ktarg -r kttest2" 1 { + "$progname: No entry for principal kttest2 exists in keytab" {} +} +klist_check "R8" $ktscratch "kttest1 $kvno1" + +setup_keytab "R9" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R9" "$ktarg -r kttest2 1" 1 { + "$progname: No entry for principal kttest2 with kvno 1 exists in keytab" {} +} +klist_check "R9" $ktscratch "kttest1 $kvno1" + +setup_keytab "R10" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R10" "$ktarg -r kttest2 all" 1 { + "$progname: No entry for principal kttest2 exists in keytab" {} +} +klist_check "R10" $ktscratch "kttest1 $kvno1" + +setup_keytab "R11" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R11" "$ktarg -r kttest1 old" 1 { + "$progname: There is only one entry for principal kttest1 in keytab" {} +} +klist_check "R11" $ktscratch "kttest1 $kvno1" + +setup_keytab "R13" $ktscratch admin admin { kttest1 kttest2 kttest1 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+1] +keytab_run "R13" "$ktarg -r kttest2 $kvno2" 0 +klist_check "R13" $ktscratch "kttest1 [expr $kvno1-1]" "kttest1 $kvno1" + +setup_keytab "R14" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+2] +keytab_run "R14" "$ktarg -r kttest1 all" 0 +klist_check "R14" $ktscratch "kttest2 [expr $kvno2-1]" "kttest2 $kvno2" + +setup_keytab "R15" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+2] +keytab_run "R15" "$ktarg -r kttest1 old" 0 +klist_check "R15" $ktscratch "kttest2 [expr $kvno2-1]" \ + "kttest1 $kvno1" "kttest2 $kvno2" + +exec rm -f $ktscratch_file diff --git a/src/kadmin/ktutil/ChangeLog b/src/kadmin/ktutil/ChangeLog index 7c8582fd1..6ca64712d 100644 --- a/src/kadmin/ktutil/ChangeLog +++ b/src/kadmin/ktutil/ChangeLog @@ -2,6 +2,10 @@ Thu Jun 13 21:42:11 1996 Tom Yu * configure.in: remove ref to SS_RULES +Fri Jul 12 14:37:47 1996 Marc Horowitz + + * configure.in (USE_KADM_LIBRARY): removed. it wasn't needed. + Tue Mar 19 19:41:31 1996 Richard Basch * ktutil_funcs.c (ktutil_write_srvtab): use any type of des key diff --git a/src/kadmin/ktutil/configure.in b/src/kadmin/ktutil/configure.in index 6be5dc2aa..7e9f3e1a0 100644 --- a/src/kadmin/ktutil/configure.in +++ b/src/kadmin/ktutil/configure.in @@ -1,7 +1,6 @@ AC_INIT(ktutil.c) CONFIG_RULES AC_PROG_INSTALL -USE_KADM_LIBRARY USE_KRB4_LIBRARY USE_SS_LIBRARY KRB5_LIBRARIES diff --git a/src/kadmin/passwd/ChangeLog b/src/kadmin/passwd/ChangeLog new file mode 100644 index 000000000..d0f4cbafc --- /dev/null +++ b/src/kadmin/passwd/ChangeLog @@ -0,0 +1,20 @@ +Mon Jul 22 04:07:02 1996 Marc Horowitz + + * tty_kpasswd.c: main returns int, not void + +Thu Jul 18 19:46:24 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Wed Jul 10 01:28:12 1996 Marc Horowitz + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 15:03:13 1996 Marc Horowitz + + * kpasswd.c, tty_kpasswd.c, xm_kpasswd.c: renamed + to + + * configure.in (CONFIG_DIRS): build the subdirs for the new admin + system, not the old one. + diff --git a/src/kadmin/passwd/Kpasswd b/src/kadmin/passwd/Kpasswd new file mode 100644 index 000000000..a7ec03161 --- /dev/null +++ b/src/kadmin/passwd/Kpasswd @@ -0,0 +1,46 @@ +*xm_ovpasswd.title: PW-CHG-GUI +*form.shadowThickness: 2 + +*foreground: black +*background: grey80 +*topShadowColor: grey95 +*bottomShadowColor: grey20 +*fontList: -*-helvetica-medium-r-*-*-14-* +*main_lbl.fontList: -*-helvetica-bold-r-*-*-14-* +*XmForm.Spacing: 5 + +*main_lbl.labelString: Changing password. +*old_lbl.labelString: Old password: +*new_lbl.labelString: New password: +*again_lbl.labelString: New password (again): +*sep.leftOffset: 0 +*sep.rightOffset: 0 +*Quit.labelString: Quit +*Help.labelString: Help + +*main_lbl.alignment: ALIGNMENT_CENTER +*lbl_form*alignment: ALIGNMENT_END +*scroll_win.shadowThickness: 0 + +*scroll_text.value: \ +Enter your old password below, and press return. You will not be able to see what you\n\ +are typing. After correctly entering your old password, you will be prompted twice for\n\ +your new password. Other messages and directions will appear in this space as necessary. +*scroll_text.rows: 5 +*scroll_text.columns: 66 +*scroll_text.scrollHorizontal: FALSE +*scroll_text.cursorPositionVisible: FALSE + +*help_dlg_popup.title: PW-CHG-GUI Help +*help_dlg.messageString: \ +Welcome to the Kerberos password changing GUI.\n\ +\n\ +In the main window, enter your old password when prompted. After verifying\n\ +your old password, the policy governing your password will be displayed, and\n\ +you will be prompted for a new password. You will then be asked to enter it\n\ +a second time, to make sure you have not made any typos. Assuming that\n\ +your new password complies with your password policy, you should receive\n\ +an acknowledgement that your password has been changed.\n\ +\n\ +If an error occurs, the process will start over from the beginning. You may\n\ +exit the application at any time by pressing the "Quit" button. diff --git a/src/kadmin/passwd/Makefile.in b/src/kadmin/passwd/Makefile.in new file mode 100644 index 000000000..46e962070 --- /dev/null +++ b/src/kadmin/passwd/Makefile.in @@ -0,0 +1,23 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -I. -DUSE_KADM5_API_VERSION=1 + +PROG = kpasswd +OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o + +all:: $(PROG).local $(PROG) + +kpasswd_strings.c kpasswd_strings.h: $(srcdir)/kpasswd_strings.et + +$(OBJS): kpasswd_strings.h + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) kpasswd_strings.c kpasswd_strings.h $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/passwd/Makefile.ov b/src/kadmin/passwd/Makefile.ov new file mode 100644 index 000000000..8841a4762 --- /dev/null +++ b/src/kadmin/passwd/Makefile.ov @@ -0,0 +1,34 @@ +TOP = .. +include $(TOP)/config.mk/template + +CFLAGS := $(CFLAGS) -DUSE_KADM5_API_VERSION=1 + +# This used as a string table, not an error table +ETABLES = kpasswd_strings.et +expand ErrorTables + +kpasswd.o: kpasswd_strings.h +depend:: kpasswd_strings.h + +PROG = kpasswd +SRCS = tty_kpasswd.c kpasswd.c kpasswd_strings.c +OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o +LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \ + $(LIBDYN) $(LIBDB) $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand NormalProgram + +ifndef OMIT_XM_KPASSWD +PROG = xm_kpasswd +CFLAGS := -I$(XM_INC) -I$(XT_INC) -I$(X_INC) $(CFLAGS) +SRCS := xm_kpasswd.c kpasswd.c kpasswd_strings.c +OBJS := xm_kpasswd.o kpasswd.o kpasswd_strings.o +LIBS := $(LIBS) $(XM_LIB) $(XT_LIB) $(X_LIB) $(REGEXLIB) + +expand NormalProgram +endif + +SUBDIRS = unit-test + +expand SubdirTarget diff --git a/src/kadmin/passwd/configure.in b/src/kadmin/passwd/configure.in new file mode 100644 index 000000000..fe1389a83 --- /dev/null +++ b/src/kadmin/passwd/configure.in @@ -0,0 +1,13 @@ +AC_INIT(kpasswd.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/passwd/kpasswd.c b/src/kadmin/passwd/kpasswd.c new file mode 100644 index 000000000..f87ad1cb0 --- /dev/null +++ b/src/kadmin/passwd/kpasswd.c @@ -0,0 +1,280 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid[] = "$Id$"; + +#include +#include + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include +#include +#include + +extern char *whoami; + +extern void display_intro_message(); +extern long read_old_password(); +extern long read_new_password(); + +#define MISC_EXIT_STATUS 6 + +/* + * Function: kpasswd + * + * Purpose: Initialize and call lower level routines to change a password + * + * Arguments: + * + * context (r) krb5_context to use + * argc/argv (r) principal name to use, optional + * read_old_password (f) function to read old password + * read_new_password (f) function to read new and change password + * display_intro_message (f) function to display intro message + * whoami (extern) argv[0] + * + * Returns: + * exit status of 0 for success + * 1 principal unknown + * 2 old password wrong + * 3 cannot initialize admin server session + * 4 new passwd mismatch or error trying to change pw + * 5 password not typed + * 6 misc error + * 7 incorrect usage + * + * Requires: + * Passwords cannot be more than 255 characters long. + * + * Effects: + * + * If argc is 2, the password for the principal specified in argv[1] + * is changed; otherwise, the principal of the default credential + * cache or username is used. display_intro_message is called with + * the arguments KPW_STR_CHANGING_PW_FOR and the principal name. + * read_old_password is then called to prompt for the old password. + * The admin system is then initialized, the principal's policy + * retrieved and explained, if appropriate, and finally + * read_new_password is called to read the new password and change the + * principal's password (presumably ovsec_kadm_chpass_principal). + * admin system is de-initialized before the function returns. + * + * Modifies: + * + * Changes the principal's password. + * + */ +int +kpasswd(context, argc, argv) + krb5_context context; + int argc; + char *argv[]; +{ + int code; + krb5_ccache ccache = NULL; + krb5_principal princ = 0; + char *princ_str; + struct passwd *pw = 0; + int pwsize; + char password[255]; /* I don't really like 255 but that's what kinit uses */ + char msg_ret[1024], admin_realm[1024]; + ovsec_kadm_principal_ent_t principal_entry = NULL; + ovsec_kadm_policy_ent_t policy_entry = NULL; + void *server_handle; + + if (argc > 2) { + com_err(whoami, KPW_STR_USAGE, 0); + return(7); + /*NOTREACHED*/ + } + + krb5_init_ets(context); + + /************************************ + * Get principal name to change * + ************************************/ + + /* Look on the command line first, followed by the default credential + cache, followed by defaulting to the Unix user name */ + + if (argc == 2) + princ_str = strdup(argv[1]); + else { + code = krb5_cc_default(context, &ccache); + /* If we succeed, find who is in the credential cache */ + if (code == 0) { + /* Get default principal from cache if one exists */ + code = krb5_cc_get_principal(context, ccache, &princ); + /* if we got a principal, unparse it, otherwise get out of the if + with an error code */ + (void) krb5_cc_close(context, ccache); + if (code == 0) { + code = krb5_unparse_name(context, princ, &princ_str); + if (code != 0) { + com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME)); + return(MISC_EXIT_STATUS); + } + } + } + + /* this is a crock.. we want to compare against */ + /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */ + /* both the file and stdio types return FCC_NOFILE. If there is */ + /* ever another ccache type (or if the error codes are ever */ + /* fixed), this code will have to be updated. */ + if (code && code != KRB5_FCC_NOFILE) { + com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC)); + return(MISC_EXIT_STATUS); + } + + /* if either krb5_cc failed check the passwd file */ + if (code != 0) { + pw = getpwuid((int) getuid()); + if (pw == NULL) { + com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE)); + return(MISC_EXIT_STATUS); + } + princ_str = strdup(pw->pw_name); + } + } + + display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str); + + /* Need to get a krb5_principal, unless we started from with one from + the credential cache */ + + if (! princ) { + code = krb5_parse_name (context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str); + free(princ_str); + return(MISC_EXIT_STATUS); + } + } + + pwsize = sizeof(password); + code = read_old_password(context, password, &pwsize); + + if (code != 0) { + memset(password, 0, sizeof(password)); + com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD)); + krb5_free_principal(context, princ); + free(princ_str); + return(MISC_EXIT_STATUS); + } + if (pwsize == 0) { + memset(password, 0, sizeof(password)); + com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ)); + krb5_free_principal(context, princ); + free(princ_str); + return(5); + } + + admin_realm[0] = '\0'; + strncat(admin_realm, krb5_princ_realm(context, princ)->data, + krb5_princ_realm(context, princ)->length); + + code = ovsec_kadm_init(princ_str, password, OVSEC_KADM_CHANGEPW_SERVICE, + admin_realm /* we probably should take a -r */ + /* someday */, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if (code != 0) { + if (code == OVSEC_KADM_BAD_PASSWORD) + com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT)); + else + com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm, + error_message(code)); + krb5_free_principal(context, princ); + free(princ_str); + return((code == OVSEC_KADM_BAD_PASSWORD)?2:3); + } + + /* Explain policy restrictions on new password if any. */ + /* Note: copy of this exists in login (kverify.c/get_verified_in_tkt). */ + + code = ovsec_kadm_get_principal(server_handle, princ, &principal_entry); + if (code != 0) { + com_err(whoami, 0, + string_text((code == OVSEC_KADM_UNK_PRINC) + ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO), + princ_str); + krb5_free_principal(context, princ); + free(princ_str); + (void) ovsec_kadm_destroy(server_handle); + return((code == OVSEC_KADM_UNK_PRINC) ? 1 : MISC_EXIT_STATUS); + } + if ((principal_entry->aux_attributes & OVSEC_KADM_POLICY) != 0) { + code = ovsec_kadm_get_policy(server_handle, + principal_entry->policy, &policy_entry); + if (code != 0) { + /* doesn't matter which error comes back, there's no nice recovery + or need to differentiate to the user */ + com_err(whoami, 0, + string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str); + (void) ovsec_kadm_free_principal_ent(server_handle, principal_entry); + krb5_free_principal(context, princ); + free(princ_str); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION), + princ_str, principal_entry->policy, + policy_entry->pw_min_length, policy_entry->pw_min_classes); + if (code = ovsec_kadm_free_principal_ent(server_handle, principal_entry)) { + (void) ovsec_kadm_free_policy_ent(server_handle, policy_entry); + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + if (code = ovsec_kadm_free_policy_ent(server_handle, policy_entry)) { + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + } + else { + /* kpasswd *COULD* output something here to encourage the choice + of good passwords, in the absence of an enforced policy. */ + if (code = ovsec_kadm_free_principal_ent(server_handle, + principal_entry)) { + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + } + + pwsize = sizeof(password); + code = read_new_password(server_handle, password, &pwsize, msg_ret, princ); + memset(password, 0, sizeof(password)); + + if (code) + com_err(whoami, 0, msg_ret); + + krb5_free_principal(context, princ); + free(princ_str); + + (void) ovsec_kadm_destroy(server_handle); + + if (code == KRB5_LIBOS_CANTREADPWD) + return(5); + else if (code) + return(4); + else + return(0); +} diff --git a/src/kadmin/passwd/kpasswd_strings.et b/src/kadmin/passwd/kpasswd_strings.et new file mode 100644 index 000000000..b78aa9d6f --- /dev/null +++ b/src/kadmin/passwd/kpasswd_strings.et @@ -0,0 +1,76 @@ +# +# Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. +# +# String table of messages for kpasswd + + +error_table kpws + +# /* M1 */ +error_code KPW_STR_USAGE, "Usage: kpasswd [principal_name]." + +error_code KPW_STR_PRIN_UNKNOWN, + "Kerberos principal name %s is not recognized." +# /* */ + +# /* M2 */ +error_code KPW_STR_WHILE_LOOKING_AT_CC, + "while reading principal name from credential cache." + +# /* M4 */ +error_code KPW_STR_OLD_PASSWORD_INCORRECT, + "Old Kerberos password is incorrect. Please try again." + +# /* M5 */ +error_code KPW_STR_CANT_OPEN_ADMIN_SERVER, +"Cannot establish a session with the Kerberos administrative server for\n\ +realm %s. %s." +# /* , . */ + +# /* M6 */ +error_code KPW_STR_NEW_PASSWORD_MISMATCH, + "New passwords do not match - password not changed.\n" + +# /* M7 */ +error_code KPW_STR_PASSWORD_CHANGED, "Kerberos password changed.\n" + +# /* M13 */ +error_code KPW_STR_PASSWORD_NOT_CHANGED, "Password not changed." + +error_code KPW_STR_PARSE_NAME, "when parsing name %s." +error_code KPW_STR_UNPARSE_NAME, "when unparsing name." +error_code KPW_STR_NOT_IN_PASSWD_FILE, "Unable to identify user from password file." + +# /* M3 */ +error_code KPW_STR_CHANGING_PW_FOR, "Changing password for %s." +# /* principal@realm */ + +error_code KPW_STR_OLD_PASSWORD_PROMPT, "Old password:" +error_code KPW_STR_WHILE_READING_PASSWORD, "while reading new password." + +# /* M4 */ +error_code KPW_STR_NO_PASSWORD_READ, +"You must type a password. Passwords must be at least one character long." + +# /* M14 */ +error_code KPW_STR_WHILE_TRYING_TO_CHANGE, "while trying to change password." + +error_code KPW_STR_WHILE_DESTROYING_ADMIN_SESSION, +"while closing session with admin server and destroying tickets." + +error_code KPW_STR_WHILE_FREEING_PRINCIPAL, +"while freeing admin principal entry" + +error_code KPW_STR_WHILE_FREEING_POLICY, +"while freeing admin policy entry" + +error_code KPW_STR_CANT_GET_POLICY_INFO, +"Could not get password policy information for principal %s." +# /* principal@realm */ + +error_code KPW_STR_POLICY_EXPLANATION, +"%s's password is controlled by the policy %s, which\nrequires a minimum of %u characters from at least %u classes (the five classes\nare lowercase, uppercase, numbers, punctuation, and all other characters)." +# /* principal_name policy_name min_length min_classes */ + +end + diff --git a/src/kadmin/passwd/tty_kpasswd.c b/src/kadmin/passwd/tty_kpasswd.c new file mode 100644 index 000000000..6d865a020 --- /dev/null +++ b/src/kadmin/passwd/tty_kpasswd.c @@ -0,0 +1,80 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid[] = "$Id$"; + +#include +#include + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include +#include +#include + +char *whoami; + +void display_intro_message(fmt_string, arg_string) + char *fmt_string; + char *arg_string; +{ + com_err(whoami, 0, fmt_string, arg_string); +} + +long read_old_password(context, password, pwsize) + krb5_context context; + char *password; + int *pwsize; +{ + long code = krb5_read_password(context, + (char *)string_text(KPW_STR_OLD_PASSWORD_PROMPT), + 0, password, pwsize); + return code; +} + +long read_new_password(server_handle, password, pwsize, msg_ret, princ) + void *server_handle; + char *password; + int *pwsize; + char *msg_ret; + krb5_principal princ; +{ + return (ovsec_kadm_chpass_principal_util(server_handle, princ, NULL, + NULL /* don't need new pw back */, + msg_ret)); +} + + +/* + * main() for tty version of kpasswd.c + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + krb5_context context; + int retval; + + initialize_kpasswd_strings(); + + whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]; + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "initializing krb5 context"); + exit(retval); + } + retval = kpasswd(context, argc, argv); + + if (!retval) + printf(string_text(KPW_STR_PASSWORD_CHANGED)); + + exit(retval); +} diff --git a/src/kadmin/passwd/unit-test/Makefile.ov b/src/kadmin/passwd/unit-test/Makefile.ov new file mode 100644 index 000000000..db042720b --- /dev/null +++ b/src/kadmin/passwd/unit-test/Makefile.ov @@ -0,0 +1,23 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +USER = root + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-body:: + $(RUNTEST) KPASSWD=../kpasswd \ + KINIT=$(TOP)/../clients/kinit/kinit \ + KDESTROY=$(TOP)/../clients/kdestroy/kdestroy \ + USER=$(USER) --tool kpasswd + +unit-test-setup:: + $(START_SERVERS) + echo "source $(TCLUTIL); catch {ovsec_kadm_init admin admin \$$OVSEC_KADM_ADMIN_SERVICE null \$$OVSEC_KADM_STRUCT_VERSION \$$OVSEC_KADM_API_VERSION_1 server_handle; ovsec_kadm_create_principal \$$server_handle [simple_principal $(USER)] {OVSEC_KADM_PRINCIPAL} $(USER); ovsec_kadm_destroy \$$server_handle;}; if {[info exists errorInfo]} { puts stderr \$$errorInfo; exit 1; }" | $(CLNTTCL) + +unit-test-cleanup:: + $(STOP_SERVERS) diff --git a/src/kadmin/passwd/unit-test/config/unix.exp b/src/kadmin/passwd/unit-test/config/unix.exp new file mode 100644 index 000000000..c77aa016a --- /dev/null +++ b/src/kadmin/passwd/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# kpasswd_version -- extract and print the version number of kpasswd +# + +proc kpasswd_version {} { + global KPASSWD + catch "exec ident $KPASSWD" tmp + if [regexp {Id: kpasswd.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$KPASSWD version $version\n" + } else { + clone_output "$KPASSWD version \n" + } +} +# +# kpasswd_load -- loads the program +# +proc kpasswd_load {} { + # +} + +# kpasswd_exit -- clean up and exit +proc kpasswd_exit {} { + # +} + +# +# kpasswd_start -- start kpasswd running +# +proc kpasswd_start { args } { + global KPASSWD + global spawn_id + + verbose "% $KPASSWD $args" 1 + eval spawn $KPASSWD $args +} diff --git a/src/kadmin/passwd/unit-test/helpers.exp b/src/kadmin/passwd/unit-test/helpers.exp new file mode 100644 index 000000000..9dcfbcf01 --- /dev/null +++ b/src/kadmin/passwd/unit-test/helpers.exp @@ -0,0 +1,217 @@ +# +# $Id$ +# + +global s +set s "\[\r\n\t\ \]" + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "starting test: $name" + + set mytest_name "$name" + + eval kpasswd_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + +proc kinit { princ pass } { + global env; + global KINIT + spawn -noecho $KINIT $princ; + + expect { + -re {Password for .*: $} + {send "$pass\n"} + timeout {puts "Timeout waiting for prompt" ; close } + } + + # this necessary so close(1) in the child will not sleep waiting for + # the parent, which is us, to read pending data. + + expect { + eof {} + } + wait +} + +proc kdestroy {} { + global KDESTROY + global errorCode errorInfo + global env + + if {[info exists errorCode]} { + set saveErrorCode $errorCode + } + if {[info exists errorInfo]} { + set saveErrorInfo $errorInfo + } + catch "system $KDESTROY 2>/dev/null" + if {[info exists saveErrorCode]} { + set errorCode $saveErrorCode + } elseif {[info exists errorCode]} { + unset errorCode + } + if {[info exists saveErrorInfo]} { + set errorInfo $saveErrorInfo + } elseif {[info exists errorInfo]} { + unset errorInfo + } +} + +global initerr_str +global initerr_regexp +set initerr_str "Cannot establish a session with the Kerberos administrative server for realm \[^\r\n\]*\\. " +set initerr_regexp "Cannot establish a session with the Kerberos administrative server for$s+realm \[^\r\n\]*\\.$s+" + +proc test_win { args name princ pass1 { pass2 "\001\001" } } { + global s + global initerr_regexp + + if { $pass2 == "\001\001" } { set pass2 "$pass1" } + + mytest "$name" $args 0 { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass1\n" } + } { + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+\[^\r\n\t\ \])\r\n" + { close; myfail "init error: $expect_out(1,string)" } + -re "^$s+New password:" + { send "$pass2\n" } + -re "^$s+.*$s+.*$s+.*$s+New password:" + { send "$pass2\n" } + } { + -re "^$s+New password \\(again\\):" + { send "$pass2\n" } + } { + -re "^$s+Kerberos password changed." + { mypass } + -re "^$s+Password changed." + { close; myfail "Wrong message on success." } + } +} + +proc test_initerr { args name princ pass status err } { + global s + global initerr_regexp + + regsub -all "$s+" $err "$s+" err2 + + mytest "$name" $args $status { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass\n" } + } { + -re "$err2" + { mypass } + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+)\r\n" + { close; myfail "init error: $expect_out(1,string)" } + } +} + +proc test_3pass { args name princ pass1 pass2 pass3 status err } { + global s + global initerr_regexp + + regsub -all "$s+" $err "$s+" err2 + + mytest "$name" $args $status { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass1\n" } + } { + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+)\r\n" + { close; myfail "init error: $expect_out(1,string)" } + -re "^$s+New password:" + { send "$pass2\n" } + -re "^$s+.*$s+.*$s+.*$s+New password:" + { send "$pass2\n" } + } { + -re "^$s+New password \\(again\\):" + { send "$pass3\n" } + } { + -re "$s+$err2" + { mypass } + } +} + diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp new file mode 100644 index 000000000..4f0354c63 --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp @@ -0,0 +1,102 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +test_3pass {test2} {D.5: different new passwords} test2 test2 test2 foobar \ + 4 {New passwords do not match - password not changed.} + +test_3pass {test2} {D.7.5: empty/empty} test2 test2 {} {} \ + 5 {You must type a password. Passwords must be at least one character long.} + +test_3pass {test2} {D.6: empty/non-empty} test2 test2 {} test2 \ + 4 {New passwords do not match - password not changed.} + +test_3pass {test2} {D.7: non-empty/empty} test2 test2 test2 {} \ + 4 {New passwords do not match - password not changed.} + + +test_win {test1} {D.8: change password} test1 test1 newpass + +test_win {test1} {D.9: test changed password} test1 newpass test1 + +mytest "D.22: No policy description was shown" test1 4 { + -re "Changing password for test1.*\\.$s+Old password:" + { send "test1\n" } +} { + -re "$s+.*$s+.*$s+.*char.*classes.*" + { myfail "policy description displayed" } + timeout { mypass } +} { + -re "^$s+New password:" + { send "newpass\n" } +} { + -re "^$s+New password \\(again\\):" + { send "ssapwen\n" } +} { + -re "$s+New passwords do not match - password not changed." + { mypass } +} + +test_3pass {pol1} {D.10: new password too short} pol1 pol111111 que que \ + 4 {New password is too short. Please choose a password which is at least [0-9]+ characters long.} + +test_3pass {pol1} {D.13: too few char classes in new password} pol1 \ + pol111111 123456789 123456789 \ + 4 {New password does not have enough character classes. The character classes are: - lower-case letters, - upper-case letters, - digits, - punctuation, and - all other characters \(e.g., control characters\). Please choose a password with at least [0-9]+ character classes.} + +test_3pass {pol1} {D.14: new password in dictionary} pol1 \ + pol111111 Discordianism Discordianism \ + 4 {New password was found in a dictionary of possible passwords and therefore may be easily guessed. Please choose another password. See the ovpasswd man page for help in choosing a good password.} + +test_win {pol1} {successful change} pol1 pol111111 polAAAAAA +# fail "successful change: XXXX password history is majorly broken" + +test_3pass {pol1} {D.11: new password same as old} pol1 \ + polAAAAAA polAAAAAA polAAAAAA \ + 4 {New password was used previously. Please choose a different password.} + +test_3pass {pol1} {D.12: new password in history} pol1 \ + polAAAAAA pol111111 pol111111 \ + 4 {New password was used previously. Please choose a different password.} + +mytest "D.18: Policy description was shown" pol1 4 { + -re "Changing password for pol1.*\\.$s+Old password:" + { send "polAAAAAA\n" } +} { + -re "$s+.*$s+.*$s+.*8 char.*2 classes.*$s+New password:" + { send "newpass1234\n" } +} { + -re "^$s+New password \\(again\\):" + { send "newpass4321\n" } +} { + -re "$s+New passwords do not match - password not changed." + { mypass } +} + +# restore pol1's password to its initial value; see discussion in +# secure-kpasswd/2204 about secure-releng/2191 if you are confused +test_win {pol1} {successful change} pol1 polAAAAAA polBBBBBB +test_win {pol1} {successful change} pol1 polBBBBBB polCCCCCC +test_win {pol1} {successful change} pol1 polCCCCCC pol111111 + +test_win {pol2} {successful change} pol2 pol222222 polbbbbbb + +test_3pass {pol2} {D.15: too soon to change password} pol2 \ + polbbbbbb pol222222 pol222222 \ + 4 {Password cannot be changed because it was changed too recently. Please wait until .*199[0-9] before you change it. If you need to change your password before then, contact your system security administrator.} + +verbose "(sleeping 30 seconds)" +catch "exec sleep 30" + +test_win {pol2} {password min life passed} pol2 polbbbbbb pol222222 + diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp new file mode 100644 index 000000000..2cda17a6a --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp @@ -0,0 +1,29 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +test_initerr {test2} {C.4: empty old password (XXXX)} test2 {} \ + 5 {You must type a password. Passwords must be at least one character long.} + +test_initerr {test2} {C.5: incorrect old password} test2 foobar \ + 2 "Old Kerberos password is incorrect. Please try again." + +# set timeout 60 +# +#test_initerr {test2@SECURE-TEST-DEAD.OV.COM} {C.8: server up, daemon down} \ +# test2 test2 \ +# 3 "" +# +#test_initerr {test2@SECURE-TEST-DOWN.OV.COM} {C.8.5: server down} \ +# test2 test2 \ +# 3 "${initerr_str}Cannot contact any KDC for requested realm" diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp new file mode 100644 index 000000000..e2bc20569 --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp @@ -0,0 +1,55 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +if {[info exists env(KRB5CCNAME)]} { + unset env(KRB5CCNAME) +} +kdestroy + +#### no principal specified + +set whoami $USER +test_win {} {B.7: default nonexisting ccache(1)} $whoami $whoami newpass +test_win {} {B.7: default nonexisting ccache(2)} $whoami newpass $whoami + +kinit test2 test2 +test_win {} {B.4: default existing cache containing existing principal} \ + test2 test2 newpass +kdestroy + +set env(KRB5CCNAME) FILE:/tmp/ovsec_adm_test_ccache +kinit test2 newpass +test_win {} {B.3: specified existing cache containing existing principal} \ + test2 newpass test2 +kdestroy +unset env(KRB5CCNAME) + +#### principal on command line + +# +test_win {test2} {B.14: existing principal, no realm} test2 test2 newpass + +# +test_initerr {bogus} {B.15, C.6: non-existent principal, no realm} bogus bogus \ + 3 "${initerr_str}Client not found in Kerberos database" + +# +test_win {test2@SECURE-TEST.OV.COM} {B.16: existing principal, with realm} \ + test2 newpass test2 + +# +test_initerr {bogus@SECURE-TEST.OV.COM} \ + {B.17: non-existent principal, with realm} \ + bogus bogus \ + 3 "${initerr_str}Client not found in Kerberos database" diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp new file mode 100644 index 000000000..e132bab2f --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp @@ -0,0 +1,26 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +mytest {A.1: two args} {foo bar} 7 { + -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass } +} + +mytest {A.2: three args} {foo bar baz} 7 { + -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass } +} + +set env(KRB5CCNAME) bogus_type:bogus_ccname +mytest {B.5: malformed ccache name} {} 6 { + -re {[a-z./]+passwd: Unknown credential cache type while reading principal name from credential cache} { mypass } +} +unset env(KRB5CCNAME) + diff --git a/src/kadmin/passwd/xm_kpasswd.c b/src/kadmin/passwd/xm_kpasswd.c new file mode 100644 index 000000000..0db1111c6 --- /dev/null +++ b/src/kadmin/passwd/xm_kpasswd.c @@ -0,0 +1,450 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid_2[] = "$Id$"; + +#include +#include + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include +#include +#include + +char *whoami; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Widget toplevel, scroll_text, prompt_text; +Widget quit_btn, help_btn, old_lbl, new_lbl, again_lbl, main_lbl; +XtAppContext app_con; +int looping; +int retval=0; + + +/*************************************************************************** + * + * A few utility functions for setting/unsetting the busy cursor + * (i.e. the watch cursor). + */ +static void +SetCursor(w,c) + Widget w; + Cursor c; +{ + while (XtIsSubclass(w, shellWidgetClass) != True) + w = XtParent(w); + + XDefineCursor(XtDisplay(w), XtWindow(w), c); + XFlush(XtDisplay(w)); +} + + +static void +SetStandardCursor() +{ + static Cursor ArrowCursor = (Cursor)NULL; + + if (ArrowCursor == (Cursor)NULL) + ArrowCursor = XCreateFontCursor(XtDisplay(toplevel), XC_top_left_arrow); + SetCursor(toplevel, ArrowCursor); +} + + +static void +SetWatchCursor() +{ + static Cursor WatchCursor = (Cursor)NULL; + + if (WatchCursor == (Cursor)NULL) + WatchCursor = XCreateFontCursor(XtDisplay(toplevel), XC_watch); + SetCursor(toplevel, WatchCursor); +} + + +/*************************************************************************** + * + * Set up a com_err hook, for displaying to a motif scrolling widget. + */ + +#if __STDC__ +# include +#else /* varargs: not STDC or no */ + /* Non-ANSI, always take path. */ +# undef VARARGS +# define VARARGS 1 +# include +#endif /* varargs */ + +static void +#ifdef __STDC__ +motif_com_err (const char *whoami, long code, const char *fmt, va_list args) +#else +motif_com_err (whoami, code, fmt, args) + const char *whoami; + long code; + const char *fmt; + va_list args; +#endif +{ + XEvent event; + char buf[2048]; + + buf[0] = '\0'; + + if (whoami) + { + strcpy(buf, whoami); + strcat(buf, ": "); + } + if (code) + { + strcat(buf, error_message(code)); + strcat(buf, " "); + } + if (fmt) + { + vsprintf(buf + strlen(buf), fmt, args); + } + + XtVaSetValues(scroll_text, XmNvalue, buf, NULL); + + for (; XtAppPending(app_con); ) + { + XtAppNextEvent(app_con, &event); + XtDispatchEvent(&event); + } +} + + +/*************************************************************************** + * + * Function to display help widget. + */ +static void +help() +{ + static Widget help_dlg = NULL; + + if (!help_dlg) + { + help_dlg = XmCreateInformationDialog(toplevel, "help_dlg", NULL, + 0); + XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_CANCEL_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_HELP_BUTTON)); + } + XtManageChild(help_dlg); +} + + +/*************************************************************************** + * + * Unset the global "looping" when we want to get out of reading a + * password. + */ +static void +unset_looping() +{ + looping = 0; +} + + +/*************************************************************************** + * + * Function to exit the gui. Callback on the "Exit" button. + */ +static void +quit() +{ + exit(retval); +} + + +/*************************************************************************** + * + * Set up motif widgets, callbacks, etc. + */ +static void +create_widgets(argc, argv) + int *argc; + char *argv[]; +{ + Widget form, lbl_form, + sep, + scroll_win; + Pixel bg; + + toplevel = XtAppInitialize(&app_con, "Kpasswd", NULL, 0, argc, argv, + NULL, NULL, 0); + form = XtCreateManagedWidget("form", xmFormWidgetClass, toplevel, NULL, 0); + quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass, + form, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtAddCallback(quit_btn, XmNactivateCallback, quit, 0); + help_btn = XtVaCreateManagedWidget("Help", xmPushButtonWidgetClass, + form, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + /* XmNshowAsDefault, TRUE, */ + NULL); + XtAddCallback(help_btn, XmNactivateCallback, help, 0); + sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, + form, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, quit_btn, + NULL); + lbl_form = XtVaCreateManagedWidget("lbl_form", xmFormWidgetClass, + form, + XmNspacing, 0, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, sep, + NULL); + old_lbl = XtVaCreateManagedWidget("old_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + new_lbl = XtVaCreateManagedWidget("new_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + again_lbl = XtVaCreateManagedWidget("again_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + prompt_text = XtVaCreateManagedWidget("prompt_text", xmTextWidgetClass, + form, + XmNeditMode, XmSINGLE_LINE_EDIT, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, lbl_form, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, sep, + NULL); + XtAddCallback(prompt_text, XmNactivateCallback, unset_looping, 0); + XtVaGetValues(prompt_text, XmNbackground, &bg, NULL); + XtVaSetValues(prompt_text, XmNforeground, bg, NULL); + + main_lbl = XtVaCreateWidget("main_lbl", xmLabelWidgetClass, + form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + scroll_win = XtVaCreateManagedWidget("scroll_win", + xmScrolledWindowWidgetClass, + form, + XmNscrollingPolicy, XmAPPLICATION_DEFINED, + XmNscrollBarDisplayPolicy, XmSTATIC, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, main_lbl, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, prompt_text, + NULL); + scroll_text = XtVaCreateManagedWidget("scroll_text", xmTextWidgetClass, + scroll_win, + XmNeditMode, XmMULTI_LINE_EDIT, + XmNeditable, FALSE, + NULL); + XtRealizeWidget(toplevel); +} + + +/*************************************************************************** + * + * + */ +static long +read_password(password, pwsize) + char *password; + int *pwsize; +{ + XEvent event; + char *text_val; + + /* OK, this next part is gross... but this is due to the fact that */ + /* this is not your traditional X program, which would be event */ + /* driven. Instead, this program is more 'CLI' in nature, so we */ + /* handle the dialogs synchronously... */ + + XtVaSetValues(prompt_text, XmNmaxLength, *pwsize, XmNvalue, "", NULL); + for (looping=1; looping; ) + { + XtAppNextEvent(app_con, &event); + XtDispatchEvent(&event); + } + XtVaGetValues(prompt_text, XmNvalue, &text_val, NULL); + *pwsize = strlen(text_val); + strcpy(password, text_val); + memset(text_val, 0, *pwsize); + XtVaSetValues(prompt_text, XmNvalue, text_val, NULL); + return(0); +} + + +/*************************************************************************** + * + * + */ +void +display_intro_message(fmt_string, arg_string) + char *fmt_string; + char *arg_string; +{ + XmString xmstr; + char buf[1024]; + + sprintf(buf, fmt_string, arg_string); + + xmstr = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET); + XtVaSetValues(main_lbl, XmNlabelString, xmstr, NULL); + XmStringFree(xmstr); + XtManageChild(main_lbl); +} + + +long +read_old_password(context, password, pwsize) + krb5_context context; + char *password; + int *pwsize; +{ + long code; + + XtManageChild(old_lbl); + code = read_password(password, pwsize); + SetWatchCursor(); + return code; +} + +long +read_new_password(server_handle, password, pwsize, msg_ret, princ) + void *server_handle; + char *password; + int *pwsize; + char *msg_ret; + krb5_principal princ; +{ + char *password2 = (char *) malloc(*pwsize * sizeof(char)); + int pwsize2 = *pwsize; + + SetStandardCursor(); + + if (password2 == NULL) + { + strcpy(msg_ret, error_message(ENOMEM)); + SetWatchCursor(); + return(ENOMEM); + } + + XtManageChild(new_lbl); XtUnmanageChild(old_lbl); + read_password(password, pwsize); + XtManageChild(again_lbl); XtUnmanageChild(new_lbl); + read_password(password2, &pwsize2); + + if (strcmp(password, password2)) + { + memset(password, 0, *pwsize); + + memset(password2, 0, pwsize2); + free(password2); + + strcpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH)); + SetWatchCursor(); + return(KRB5_LIBOS_BADPWDMATCH); + } + + memset(password2, 0, pwsize2); + free(password2); + + SetWatchCursor(); + return (ovsec_kadm_chpass_principal_util(server_handle, princ, password, + NULL /* don't need new pw back */, + msg_ret)); +} + + +/*************************************************************************** + * + * + */ +void +main(argc, argv) + int argc; + char *argv[]; +{ + krb5_context context; + int code; + + initialize_kpasswd_strings(); + + whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]; + + (void) set_com_err_hook(motif_com_err); + + create_widgets(&argc, argv); + XmProcessTraversal(prompt_text, XmTRAVERSE_CURRENT); + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "initializing krb5 context"); + exit(retval); + } + + while (1) + { + retval = kpasswd(context, argc, argv); + SetStandardCursor(); + + if (!retval) + com_err(0, 0, string_text(KPW_STR_PASSWORD_CHANGED)); + + if (retval == 0) /* 0 is success, so presumably the user */ + /* is done. */ + XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT); + + if ((retval == 1) || /* the rest are "fatal", so we should */ + (retval == 3) || /* "force" the user to quit... */ + (retval == 6) || + (retval == 7)) + { + XtSetSensitive(prompt_text, FALSE); + XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT); + XtAppMainLoop(app_con); + } + } + + /* NOTREACHED */ + exit(retval); +} diff --git a/src/kadmin/scripts/inst-hdrs.sh b/src/kadmin/scripts/inst-hdrs.sh new file mode 100644 index 000000000..242be89e9 --- /dev/null +++ b/src/kadmin/scripts/inst-hdrs.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +dir=$1; shift +while [ $# -gt 0 ]; do + file=$1 + cmp -s $file $dir/$file + if [ $? != 0 ]; then + echo "+ rm $dir/$file" + rm -f $dir/$file + echo "+ cp $file $dir/$file" + cp $file $dir/$file + fi + shift +done diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in new file mode 100644 index 000000000..41bea4f45 --- /dev/null +++ b/src/kadmin/server/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadmind +OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/server/Makefile.ov b/src/kadmin/server/Makefile.ov new file mode 100644 index 000000000..4f8e489fb --- /dev/null +++ b/src/kadmin/server/Makefile.ov @@ -0,0 +1,39 @@ +TOP = .. +include $(TOP)/config.mk/template +CFLAGS := $(CFLAGS) + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 +endif + +PROG := kadmind +SRCS := kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c misc.c server_glue_v1.c +OBJS := kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o +LIBS := $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBDYN) \ + $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDB) \ + $(NDBMLIB) $(NETLIB) $(BSDLIB) $(REGEXLIB) + +expand InstallServer +expand Depend + +clean:: + $(CLEAN) acls.c + +expand Saber + +SABER_LIBS := $(LIBDB) $(LIBGSSAPI_KRB5) $(LIBDYN) $(LIBKDB5) \ + $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR) +SABER_FLAGS := -G + +saber:: + #cd ../../rpc + #make saber + #cd ../admin/lib/adb + #make saber + #cd ../common + #make saber + #cd ../server + #make saber + #cd ../../server + #load /usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.4.5/libgcc.a + #load $(SABER_FLAGS) $(LDFLAGS) $(GSSLIB) $(SABER_LIBS) diff --git a/src/kadmin/server/acls.l b/src/kadmin/server/acls.l new file mode 100644 index 000000000..aee4801e9 --- /dev/null +++ b/src/kadmin/server/acls.l @@ -0,0 +1,190 @@ +%{ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.3 1996/07/22 20:28:49 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.2.4.1 1996/07/18 03:03:31 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.2.2.1 1996/06/20 21:56:31 marc + * File added to the repository on a branch + * + * Revision 1.2 1993/11/05 07:47:46 bjaspan + * add and use cmp_gss_names, fix regexp bug + * + * Revision 1.1 1993/11/05 07:08:48 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +enum tokens { + NEWLINE = 257, + COMMA, + SEMI, + + GET = 300, + ADD, + MODIFY, + DELETE, + + ID = 350, +}; + +typedef union { + char *s; +} toktype; + +toktype tokval; +int acl_lineno = 0; + +%} + +%% + +\n acl_lineno++; +[ \t]* ; +[ ]*#.* ; +"," return (COMMA); +";" return (SEMI); +"get" return (GET); +"add" return (ADD); +"modify" return (MODIFY); +"delete" return (DELETE); +^[^ \t\n]+ { tokval.s = yytext; return (ID); } + +%% + +#include +#include +#include +#include +#include + +typedef struct _entry { + gss_name_t gss_name; + char *name; + u_int privs; + struct _entry *next; +} acl_entry; + +static acl_entry *acl_head = NULL; + +static void error(char *msg); + +int parse_aclfile(FILE *acl_file) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc in_buf; + acl_entry *entry; + enum tokens tok; + + yyin = acl_file; + + acl_lineno = 1; + while ((tok = yylex()) != 0) { + if (tok != ID) { + error("expected identifier"); + goto error; + } + + entry = (acl_entry *) malloc(sizeof(acl_entry)); + if (entry == NULL) { + error("out of memory"); + goto error; + } + entry->name = strdup(tokval.s); + entry->privs = 0; + while (1) { + switch (tok = yylex()) { + case GET: + entry->privs |= OVSEC_KADM_PRIV_GET; + break; + case ADD: + entry->privs |= OVSEC_KADM_PRIV_ADD; + break; + case MODIFY: + entry->privs |= OVSEC_KADM_PRIV_MODIFY; + break; + case DELETE: + entry->privs |= OVSEC_KADM_PRIV_DELETE; + break; + default: + error("expected privilege"); + goto error; + } + tok = yylex(); + if (tok == COMMA) + continue; + else if (tok == SEMI) + break; + else { + error("expected comma or semicolon"); + goto error; + } + } + + in_buf.value = entry->name; + in_buf.length = strlen(entry->name) + 1; + gssstat = gss_import_name(&minor_stat, &in_buf, + gss_nt_krb5_name, &entry->gss_name); + if (gssstat != GSS_S_COMPLETE) { + error("invalid name"); + goto error; + } + + if (acl_head == NULL) { + entry->next = NULL; + acl_head = entry; + } else { + entry->next = acl_head; + acl_head = entry; + } + } + return 0; + +error: + return 1; +} + +int acl_check(gss_name_t caller, int priv) +{ + acl_entry *entry; + + entry = acl_head; + while (entry) { + if (cmp_gss_names(entry->gss_name, caller) && entry->privs & priv) + return 1; + entry = entry->next; + } + return 0; +} + +int cmp_gss_names(gss_name_t name1, gss_name_t name2) +{ + OM_uint32 minor_stat; + int eq; + (void) gss_compare_name(&minor_stat, name1, name2, &eq); + return eq; +} + +static void error(char *msg) +{ + syslog(LOG_ERR, "Error while parsing acl file, line %d: %s\n", + acl_lineno, msg); +} + +yywrap() { return(1); } diff --git a/src/kadmin/server/configure.in b/src/kadmin/server/configure.in new file mode 100644 index 000000000..98492f909 --- /dev/null +++ b/src/kadmin/server/configure.in @@ -0,0 +1,17 @@ +AC_INIT(ovsec_kadmd.c) +CONFIG_RULES +AC_PROG_INSTALL +dnl AC_CHECK_FUNCS(waitpid vsprintf) +dnl AC_CHECK_HEADERS(sys/select.h) +dnl CHECK_SIGNALS +dnl CHECK_SETJMP +dnl CHECK_WAIT_TYPE +dnl ET_RULES +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c new file mode 100644 index 000000000..9128821d5 --- /dev/null +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -0,0 +1,248 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.12 1996/07/22 20:28:53 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.11.4.1 1996/07/18 03:03:35 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.11.2.2 1996/07/09 20:07:57 marc + * * kadm_rpc_svc.c: renamed to + * + * Revision 1.11.2.1 1996/06/20 21:56:44 marc + * File added to the repository on a branch + * + * Revision 1.11 1996/06/17 19:49:28 bjaspan + * use krb5_klog_syslog + * + * Revision 1.10 1996/05/29 21:07:53 bjaspan + * be a bit more loud when warning, and don't exit when args can't be freed + * + * Revision 1.9 1996/05/20 21:34:56 bjaspan + * log an error when sendreply fails + * + * Revision 1.8 1996/05/12 07:06:23 marc + * - fixup includes to match beta6 + * + * Revision 1.7 1995/08/01 19:25:59 bjaspan + * [secure/1318] allow retrieval of some/all principal/policy names + * + * Revision 1.6 1994/09/20 16:25:33 bjaspan + * [secure-admin/2436: API versioning fixes to various admin files] + * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes] + * + * Sandbox: + * + * More API versioning stuff -- need to add api_version field to RPC + * return structures in addition to calling structures. + * + * Revision 1.6 1994/09/12 20:19:16 jik + * More API versioning stuff -- need to add api_version field to RPC + * return structures in addition to calling structures. + * + * Revision 1.5 1994/08/16 18:55:46 jik + * Versioning changes. + * + * Revision 1.4 1994/04/25 17:05:05 bjaspan + * [secure-admin/1832] accept old gssapi number, log error when number + * is wrong + * + * Revision 1.3 1993/11/15 02:30:54 shanzer + * added funky procedure header comments. + * + * Revision 1.2 1993/11/10 23:11:21 bjaspan + * added getprivs + * + * Revision 1.1 1993/11/05 07:09:00 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include + +/* + * Function: kadm_1 + * + * Purpose: RPC proccessing procedure. + * originally generated from rpcgen + * + * Arguments: + * rqstp (input) rpc request structure + * transp (input) rpc transport structure + * (input/output) + * + * + * Requires: + * Effects: + * Modifies: + */ + +void kadm_1(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + cprinc_arg create_principal_1_arg; + dprinc_arg delete_principal_1_arg; + mprinc_arg modify_principal_1_arg; + rprinc_arg rename_principal_1_arg; + gprinc_arg get_principal_1_arg; + chpass_arg chpass_principal_1_arg; + chrand_arg chrand_principal_1_arg; + cpol_arg create_policy_1_arg; + dpol_arg delete_policy_1_arg; + mpol_arg modify_policy_1_arg; + gpol_arg get_policy_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + if (rqstp->rq_cred.oa_flavor != AUTH_GSSAPI && + rqstp->rq_cred.oa_flavor != AUTH_GSSAPI_COMPAT) { + krb5_klog_syslog(LOG_ERR, "Authentication attempt failed: %s, invalid " + "RPC authentication flavor %d", + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr), + rqstp->rq_cred.oa_flavor); + svcerr_weakauth(transp); + return; + } + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + return; + + case CREATE_PRINCIPAL: + xdr_argument = xdr_cprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) create_principal_1; + break; + + case DELETE_PRINCIPAL: + xdr_argument = xdr_dprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) delete_principal_1; + break; + + case MODIFY_PRINCIPAL: + xdr_argument = xdr_mprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) modify_principal_1; + break; + + case RENAME_PRINCIPAL: + xdr_argument = xdr_rprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) rename_principal_1; + break; + + case GET_PRINCIPAL: + xdr_argument = xdr_gprinc_arg; + xdr_result = xdr_gprinc_ret; + local = (char *(*)()) get_principal_1; + break; + + case GET_PRINCS: + xdr_argument = xdr_gprincs_arg; + xdr_result = xdr_gprincs_ret; + local = (char *(*)()) get_princs_1; + break; + + case CHPASS_PRINCIPAL: + xdr_argument = xdr_chpass_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) chpass_principal_1; + break; + + case CHRAND_PRINCIPAL: + xdr_argument = xdr_chrand_arg; + xdr_result = xdr_chrand_ret; + local = (char *(*)()) chrand_principal_1; + break; + + case CREATE_POLICY: + xdr_argument = xdr_cpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) create_policy_1; + break; + + case DELETE_POLICY: + xdr_argument = xdr_dpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) delete_policy_1; + break; + + case MODIFY_POLICY: + xdr_argument = xdr_mpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) modify_policy_1; + break; + + case GET_POLICY: + xdr_argument = xdr_gpol_arg; + xdr_result = xdr_gpol_ret; + local = (char *(*)()) get_policy_1; + break; + + case GET_POLS: + xdr_argument = xdr_gpols_arg; + xdr_result = xdr_gpols_ret; + local = (char *(*)()) get_pols_1; + break; + + case GET_PRIVS: + xdr_argument = xdr_u_int32; + xdr_result = xdr_getprivs_ret; + local = (char *(*)()) get_privs_1; + break; + + case INIT: + xdr_argument = xdr_u_int32; + xdr_result = xdr_generic_ret; + local = (char *(*)()) init_1; + break; + + default: + krb5_klog_syslog(LOG_ERR, "Invalid OVSEC_KADM procedure number: %s, %d", + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr), + rqstp->rq_proc); + svcerr_noproc(transp); + return; + } + memset((char *)&argument, 0, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to send function results, " + "continuing."); + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free arguments, " + "continuing."); + } + return; +} diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c new file mode 100644 index 000000000..9dc3d9d28 --- /dev/null +++ b/src/kadmin/server/misc.c @@ -0,0 +1,138 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "misc.h" + +/* + * Function: chpass_principal_wrapper + * + * Purpose: wrapper to kadm5_chpass_principal that checks to see if + * pw_min_life has been reached. if not it returns an error. + * otherwise it calls kadm5_chpass_principal + * + * Arguments: + * principal (input) krb5_principals whose password we are + * changing + * passoword (input) passowrd we are going to change to. + * 0 on sucsess error code on failure. + * + * Requires: + * kadm5_init to have been run. + * + * Effects: + * calls kadm5_chpass_principal which changes the kdb and the + * the admin db. + * + */ +kadm5_ret_t +chpass_principal_wrapper(void *server_handle, + krb5_principal principal, char *password) +{ + krb5_int32 now; + kadm5_ret_t ret; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + kadm5_server_handle_t handle = server_handle; + + if (ret = krb5_timeofday(handle->context, &now)) + return ret; + + if((ret = kadm5_get_principal(handle->lhandle, principal, + &princ, + KADM5_PRINCIPAL_NORMAL_MASK)) != + KADM5_OK) + return ret; + if(princ.aux_attributes & KADM5_POLICY) { + if((ret=kadm5_get_policy(handle->lhandle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) kadm5_free_policy_ent(handle->lhandle, &pol); + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return KADM5_PASS_TOOSOON; + } + if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + } + if (ret = kadm5_free_principal_ent(handle->lhandle, &princ)) + return ret; + + return kadm5_chpass_principal(server_handle, principal, password); +} + + +/* + * Function: randkey_principal_wrapper + * + * Purpose: wrapper to kadm5_randkey_principal which checks the + passwords min. life. + * + * Arguments: + * principal (input) krb5_principal whose password we are + * changing + * key (output) new random key + * 0, error code on error. + * + * Requires: + * kadm5_init needs to be run + * + * Effects: + * calls kadm5_randkey_principal + * + */ +kadm5_ret_t +randkey_principal_wrapper(void *server_handle, + krb5_principal principal, + krb5_keyblock **keys, int *n_keys) +{ + + krb5_int32 now; + kadm5_ret_t ret; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + kadm5_server_handle_t handle = server_handle; + + if (ret = krb5_timeofday(handle->context, &now)) + return ret; + + if((ret = kadm5_get_principal(handle->lhandle, + principal, &princ, + KADM5_PRINCIPAL_NORMAL_MASK)) != + OSA_ADB_OK) + return ret; + if(princ.aux_attributes & KADM5_POLICY) { + if((ret=kadm5_get_policy(handle->lhandle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) kadm5_free_policy_ent(handle->lhandle, &pol); + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return KADM5_PASS_TOOSOON; + } + if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + } + if (ret = kadm5_free_principal_ent(handle->lhandle, &princ)) + return ret; + return kadm5_randkey_principal(server_handle, principal, keys, n_keys); +} diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h new file mode 100644 index 000000000..c92f5fe32 --- /dev/null +++ b/src/kadmin/server/misc.h @@ -0,0 +1,53 @@ +/* + * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.6 1996/07/22 20:28:56 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.5.4.1 1996/07/18 03:03:40 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.5.2.1 1996/06/20 21:57:20 marc + * File added to the repository on a branch + * + * Revision 1.5 1996/05/30 21:13:24 bjaspan + * kadm5_get_principal_v1 takes a kadm5_principal_ent_t_v1 + * add kadm5_get_policy_v1 + * + * Revision 1.4 1996/05/20 21:39:05 bjaspan + * rename to kadm5 + * add kadm5_get_principal_v1 + * + * Revision 1.3 1994/09/13 18:24:41 jik + * Back out randkey changes. + * + * Revision 1.2 1994/09/12 20:26:12 jik + * randkey_principal_wrapper now takes a new_kvno option. + * + * Revision 1.1 1994/08/11 17:00:44 jik + * Initial revision + * + */ + +kadm5_ret_t chpass_principal_wrapper(void *server_handle, + krb5_principal principal, + char *password); + +kadm5_ret_t randkey_principal_wrapper(void *server_handle, + krb5_principal principal, + krb5_keyblock **key, + int *n_keys); + +kadm5_ret_t kadm5_get_principal_v1(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t_v1 *ent); + +kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, + kadm5_policy_ent_t *ent); diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c new file mode 100644 index 000000000..34f233893 --- /dev/null +++ b/src/kadmin/server/ovsec_kadmd.c @@ -0,0 +1,762 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#ifdef _AIX +#include +#endif +#include +#include +#include +#include +#include /* inet_ntoa */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef PURIFY +#include "purify.h" + +int signal_pure_report = 0; +int signal_pure_clear = 0; +void request_pure_report(int); +void request_pure_clear(int); +#endif /* PURIFY */ + +int signal_request_exit = 0; +int signal_request_reset = 0; +void request_exit(int); +void request_reset_db(int); +void reset_db(void); +void sig_pipe(int); +void kadm_svc_run(void); + +#define TIMEOUT 15 + +gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; +void *global_server_handle; + +/* + * This is a kludge, but the server needs these constants to be + * compatible with old clients. They are defined in , + * but only if USE_KADM5_API_VERSION == 1. + */ +#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin" +#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" + +/* + * This enables us to set the keytab that gss_acquire_cred uses, but + * it also restricts us to linking against the Kv5 GSS-API library. + * Since this is *k*admind, that shouldn't be a problem. + */ +extern char *krb5_defkeyname; + +char *build_princ_name(char *name, char *realm); +void log_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, char *data); +void log_badverf(gss_name_t client_name, gss_name_t server_name, + struct svc_req *rqst, struct rpc_msg *msg, + char *data); +void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char + *error, char *data); +void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor); +void log_badauth_display_status_1(char *m, OM_uint32 code, int type, + int rec); + + +/* + * Function: usage + * + * Purpose: print out the server usage message + * + * Arguments: + * Requires: + * Effects: + * Modifies: + */ + +void usage() +{ + fprintf(stderr, "Usage: kadmind [-r realm] [-m] [-nofork] " + "[-port port-number]\n"); + exit(1); +} + +/* XXX yuck. the signal handlers need this */ +static krb5_context context; + +int main(int argc, char *argv[]) +{ + void kadm_1(struct svc_req *, SVCXPRT *); + register SVCXPRT *transp; + extern char *optarg; + extern int optind, opterr; + int ret, rlen, nofork, oldnames = 0; + OM_uint32 OMret; + char *whoami; + FILE *acl_file; + gss_buffer_desc in_buf; + struct servent *srv; + struct sockaddr_in addr; + int s; + short port = 0; + auth_gssapi_name names[4]; + kadm5_config_params params; + + names[0].name = names[1].name = names[2].name = names[3].name = NULL; + names[0].type = names[1].type = names[2].type = names[3].type = + gss_nt_krb5_name; + +#ifdef PURIFY + purify_start_batch(); +#endif /* PURIFY */ + whoami = argv[0]; + + nofork = 0; + + memset((char *) ¶ms, 0, sizeof(params)); + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-r") == 0) { + argc--; argv++; + if (!argc) + usage(); + params.realm = *argv; + params.mask |= KADM5_CONFIG_REALM; + argc--; argv++; + continue; + } else if (strcmp(*argv, "-m") == 0) { + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + } else if (strcmp(*argv, "-nofork") == 0) { + nofork = 1; + } else if(strcmp(*argv, "-port") == 0) { + argc--; argv++; + if(!argc) + usage(); + params.kadmind_port = atoi(*argv); + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } else + break; + argc--; argv++; + } + + if (argc != 0) + usage(); + + if (ret = krb5_init_context(&context)) { + fprintf(stderr, "%s: %s while initializing context, aborting\n", + whoami, error_message(ret)); + exit(1); + } + + krb5_klog_init(context, "admin_server", whoami, 1); + + if((ret = kadm5_init("kadmind", NULL, + NULL, ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &global_server_handle)) != + KADM5_OK) { + krb5_klog_syslog(LOG_ERR, "%s while initializing, aborting", + error_message(ret)); + fprintf(stderr, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + krb5_klog_close(); + exit(1); + } + + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + fprintf(stderr, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \ + KADM5_CONFIG_ADMIN_KEYTAB) + + if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", whoami, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + fprintf(stderr, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", whoami, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + krb5_klog_close(); + kadm5_destroy(global_server_handle); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(params.kadmind_port); + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s", + error_message(errno)); + fprintf(stderr, "Cannot create TCP socket: %s", + error_message(errno)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + #ifdef SO_REUSEADDR + /* the old admin server turned on SO_REUSEADDR for non-default + port numbers. this was necessary, on solaris, for the tests + to work. jhawk argues that the debug and production modes + should be the same. I think I agree, so I'm always going to set + SO_REUSEADDR. The other option is to have the unit tests wait + until the port is useable, or use a different port each time. + --marc */ + + { + int allowed; + + allowed = 1; + if (setsockopt(s, + SOL_SOCKET, + SO_REUSEADDR, + (char *) &allowed, + sizeof(allowed)) < 0) { + krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s", + error_message(errno)); + fprintf(stderr, "Cannot set SO_REUSEADDR: %s", + error_message(errno)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + } + #endif + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + int oerrno = errno; + fprintf(stderr, "%s: Cannot bind socket.\n", whoami); + fprintf(stderr, "bind: %s\n", error_message(oerrno)); + errno = oerrno; + krb5_klog_syslog(LOG_ERR, "Cannot bind socket: %m"); + if(oerrno == EADDRINUSE) { + char *w = strrchr(whoami, '/'); + if (w) { + w++; + } + else { + w = whoami; + } + fprintf(stderr, +"This probably means that another %s process is already\n" +"running, or that another program is using the server port (number %d)\n" +"after being assigned it by the RPC portmap deamon. If another\n" +"%s is already running, you should kill it before\n" +"restarting the server. If, on the other hand, another program is\n" +"using the server port, you should kill it before running\n" +"%s, and ensure that the conflict does not occur in the\n" +"future by making sure that %s is started on reboot\n" + "before portmap.\n", w, ntohs(addr.sin_port), w, w, w); + krb5_klog_syslog(LOG_ERR, "Check for already-running %s or for " + "another process using port %d", w, + htons(addr.sin_port)); + } + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + transp = svctcp_create(s, 0, 0); + if(transp == NULL) { + fprintf(stderr, "%s: Cannot create RPC service.\n", whoami); + krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m"); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) { + fprintf(stderr, "%s: Cannot register RPC service.\n", whoami); + krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, failing."); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); + names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); + names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm); + names[3].name = build_princ_name(OVSEC_KADM_CHANGEPW_SERVICE, + params.realm); + if (names[0].name == NULL || names[1].name == NULL || + names[2].name == NULL || names[3].name == NULL) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, " + "failing."); + fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n", + whoami); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + krb5_defkeyname = params.admin_keytab; + + /* + * Try to acquire creds for the old OV services as well as the + * new names, but if that fails just fall back on the new names. + */ + if (_svcauth_gssapi_set_names(names, 4) == TRUE) + oldnames++; + if (!oldnames && _svcauth_gssapi_set_names(names, 2) == FALSE) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, " + "failing."); + fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n", + whoami); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + /* if set_names succeeded, this will too */ + in_buf.value = names[1].name; + in_buf.length = strlen(names[1].name) + 1; + (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name, + &gss_changepw_name); + if (oldnames) { + in_buf.value = names[3].name; + in_buf.length = strlen(names[3].name) + 1; + (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name, + &gss_oldchangepw_name); + } + + _svcauth_gssapi_set_log_badauth_func(log_badauth, NULL); + _svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); + _svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); + + if (ret = acl_init(context, 0, params.acl_file)) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize acl file: %s", + error_message(ret)); + fprintf(stderr, "%s: Cannot initialize acl file: %s\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + if (!nofork && (ret = daemon(0, 0))) { + ret = errno; + krb5_klog_syslog(LOG_ERR, "Cannot detach from tty: %s", error_message(ret)); + fprintf(stderr, "%s: Cannot detach from tty: %s\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + signal(SIGINT, request_exit); + signal(SIGTERM, request_exit); + signal(SIGQUIT, request_exit); + signal(SIGHUP, request_reset_db); + signal(SIGPIPE, sig_pipe); +#ifdef PURIFY + signal(SIGUSR1, request_pure_report); + signal(SIGUSR2, request_pure_clear); +#endif /* PURIFY */ + krb5_klog_syslog(LOG_INFO, "starting"); + + kadm_svc_run(); + krb5_klog_syslog(LOG_INFO, "finished, exiting"); + kadm5_destroy(global_server_handle); + close(s); + krb5_klog_close(); + exit(2); +} + +/* + * Function: kadm_svc_run + * + * Purpose: modified version of sunrpc svc_run. + * which closes the database every TIMEOUT seconds. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + */ + +void kadm_svc_run(void) +{ + fd_set rfd; + int sz = _rpc_dtablesize(); + struct timeval timeout; + + while(signal_request_exit == 0) { + if (signal_request_reset) + reset_db(); +#ifdef PURIFY + if (signal_pure_report) /* check to see if a report */ + /* should be dumped... */ + { + purify_new_reports(); + signal_pure_report = 0; + } + if (signal_pure_clear) /* ...before checking whether */ + /* the info should be cleared. */ + { + purify_clear_new_reports(); + signal_pure_clear = 0; + } +#endif /* PURIFY */ + timeout.tv_sec = TIMEOUT; + timeout.tv_usec = 0; + rfd = svc_fdset; + switch(select(sz, (fd_set *) &rfd, NULL, NULL, &timeout)) { + case -1: + if(errno == EINTR) + continue; + perror("select"); + return; + case 0: + reset_db(); + break; + default: + svc_getreqset(&rfd); + } + } +} + +#ifdef PURIFY +/* + * Function: request_pure_report + * + * Purpose: sets flag saying the server got a signal and that it should + * dump a purify report when convenient. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_pure_report to one + */ + +void request_pure_report(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report"); + signal_pure_report = 1; + return; +} + +/* + * Function: request_pure_clear + * + * Purpose: sets flag saying the server got a signal and that it should + * dump a purify report when convenient, then clear the + * purify tables. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_pure_report to one + * sets signal_pure_clear to one + */ + +void request_pure_clear(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report and clear the old Purify info"); + signal_pure_report = 1; + signal_pure_clear = 1; + return; +} +#endif /* PURIFY */ + +/* + * Function: request_reset_db + * + * Purpose: sets flag saying the server got a signal and that it should + * reset the database files when convenient. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_request_reset to one + */ + +void request_reset_db(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request resetting the databases"); + signal_request_reset = 1; + return; +} + +/* + * Function: reset-db + * + * Purpose: flushes the currently opened database files to disk. + * + * Arguments: + * Requires: + * Effects: + * + * Currently, just sets signal_request_reset to 0. The kdb and adb + * libraries used to be sufficiently broken that it was prudent to + * close and reopen the databases periodically. They are no longer + * that broken, so this function is not necessary. + */ +void reset_db(void) +{ +#ifdef notdef + kadm5_ret_t ret; + + if (ret = kadm5_flush(global_server_handle)) { + krb5_klog_syslog(LOG_ERR, "FATAL ERROR! %s while flushing databases. " + "Databases may be corrupt! Aborting.", + error_message(ret)); + krb5_klog_close(); + exit(3); + } +#endif + + signal_request_reset = 0; + return; +} + +/* + * Function: request-exit + * + * Purpose: sets flags saying the server got a signal and that it + * should exit when convient. + * + * Arguments: + * Requires: + * Effects: + * modifies signal_request_exit which ideally makes the server exit + * at some point. + * + * Modifies: + * signal_request_exit + */ + +void request_exit(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request exit"); + signal_request_exit = 1; + return; +} + +/* + * Function: sig_pipe + * + * Purpose: SIGPIPE handler + * + * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns, + * thus causing the read() or write() to fail and, presumable, the RPC + * to recover. Otherwise, the process aborts. + */ +void sig_pipe(int unused) +{ + krb5_klog_syslog(LOG_NOTICE, "Warning: Received a SIGPIPE; probably a " + "client aborted. Continuing."); + return; +} + +/* + * Function: build_princ_name + * + * Purpose: takes a name and a realm and builds a string that can be + * consumed by krb5_parse_name. + * + * Arguments: + * name (input) name to be part of principal + * realm (input) realm part of principal + * char * pointing to "name@realm" + * + * Requires: + * name be non-null. + * + * Effects: + * Modifies: + */ + +char *build_princ_name(char *name, char *realm) +{ + char *fullname; + + fullname = (char *) malloc(strlen(name) + 1 + + (realm ? strlen(realm) + 1 : 0)); + if (fullname == NULL) + return NULL; + if (realm) + sprintf(fullname, "%s@%s", name, realm); + else + strcpy(fullname, name); + return fullname; +} + +/* + * Function: log_badverf + * + * Purpose: Call from GSS-API Sun RPC for garbled/forged/replayed/etc + * messages. + * + * Argiments: + * client_name (r) GSS-API client name + * server_name (r) GSS-API server name + * rqst (r) RPC service request + * msg (r) RPC message + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the invalid request via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_badverf(gss_name_t client_name, gss_name_t server_name, + struct svc_req *rqst, struct rpc_msg *msg, char + *data) +{ + static const char *const proc_names[] = { + "kadm5_create_principal", + "kadm5_delete_principal", + "kadm5_modify_principal", + "kadm5_rename_principal", + "kadm5_get_principal", + "kadm5_chpass_principal", + "kadm5_randkey_principal", + "kadm5_create_policy", + "kadm5_delete_policy", + "kadm5_modify_policy", + "kadm5_get_policy", + "kadm5_get_privs", + }; + OM_uint32 minor; + gss_buffer_desc client, server; + gss_OID gss_type; + char *a; + + (void) gss_display_name(&minor, client_name, &client, &gss_type); + (void) gss_display_name(&minor, server_name, &server, &gss_type); + a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr); + + krb5_klog_syslog(LOG_NOTICE, "WARNING! Forged/garbled request: %s, " + "claimed client = %s, server = %s, addr = %s", + proc_names[msg->rm_call.cb_proc], client.value, + server.value, a); + + (void) gss_release_buffer(&minor, &client); + (void) gss_release_buffer(&minor, &server); +} + +/* + * Function: log_miscerr + * + * Purpose: Callback from GSS-API Sun RPC for miscellaneous errors + * + * Arguments: + * rqst (r) RPC service request + * msg (r) RPC message + * error (r) error message from RPC + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the error via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, + char *error, char *data) +{ + char *a; + + a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr); + krb5_klog_syslog(LOG_NOTICE, "Miscellaneous RPC error: %s, %s", a, error); +} + + + +/* + * Function: log_badauth + * + * Purpose: Callback from GSS-API Sun RPC for authentication + * failures/errors. + * + * Arguments: + * major (r) GSS-API major status + * minor (r) GSS-API minor status + * addr (r) originating address + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the GSS-API error via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, char *data) +{ + char *a; + + /* Authentication attempt failed: , */ + + a = inet_ntoa(addr->sin_addr); + + krb5_klog_syslog(LOG_NOTICE, "Authentication attempt failed: %s, GSS-API " + "error strings are:", a); + log_badauth_display_status(" ", major, minor); + krb5_klog_syslog(LOG_NOTICE, " GSS-API error strings complete.\n"); +} + +void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor) +{ + log_badauth_display_status_1(msg, major, GSS_C_GSS_CODE, 0); + log_badauth_display_status_1(msg, minor, GSS_C_MECH_CODE, 0); +} + +void log_badauth_display_status_1(char *m, OM_uint32 code, int type, + int rec) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc msg; + int msg_ctx; + + msg_ctx = 0; + while (1) { + gssstat = gss_display_status(&minor_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (gssstat != GSS_S_COMPLETE) { + if (!rec) { + log_badauth_display_status_1(m,gssstat,GSS_C_GSS_CODE,1); + log_badauth_display_status_1(m, minor_stat, + GSS_C_MECH_CODE, 1); + } else + krb5_klog_syslog(LOG_ERR, "GSS-API authentication error %s: " + "recursive failure!\n", msg); + return; + } + + krb5_klog_syslog(LOG_NOTICE, "%s %s\n", m, (char *)msg.value); + (void) gss_release_buffer(&minor_stat, &msg); + + if (!msg_ctx) + break; + } +} diff --git a/src/kadmin/server/server_glue_v1.c b/src/kadmin/server/server_glue_v1.c new file mode 100644 index 000000000..c0bec26e6 --- /dev/null +++ b/src/kadmin/server/server_glue_v1.c @@ -0,0 +1,31 @@ +#define USE_KADM5_API_VERSION 1 +#include + +/* + * In server_stubs.c, kadmind has to be able to call kadm5 functions + * with the arguments appropriate for any api version. Because of the + * prototypes in admin.h, however, the compiler will only allow one + * set of arguments to be passed. This file exports the old api + * definitions with a different name, so they can be called from + * server_stubs.c, and just passes on the call to the real api + * function; it uses the old api version, however, so it can actually + * call the real api functions whereas server_stubs.c cannot. + * + * This is most useful for functions like kadm5_get_principal that + * take a different number of arguments based on API version. For + * kadm5_get_policy, the same thing could be accomplished with + * typecasts instead. + */ + +kadm5_ret_t kadm5_get_principal_v1(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t_v1 *ent) +{ + return kadm5_get_principal(server_handle, principal, ent); +} + +kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, + kadm5_policy_ent_t *ent) +{ + return kadm5_get_policy(server_handle, name, ent); +} diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c new file mode 100644 index 000000000..8107160af --- /dev/null +++ b/src/kadmin/server/server_stubs.c @@ -0,0 +1,1045 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include /* for gss_nt_krb5_name */ +#include +#include +#include +#include +#include +#include +#include "misc.h" + +#define LOG_UNAUTH "Unauthorized request: %s, %s, client=%s, service=%s, addr=%s" +#define LOG_DONE "Request: %s, %s, %s, client=%s, service=%s, addr=%s" + +extern gss_name_t gss_changepw_name; +extern gss_name_t gss_oldchangepw_name; +extern void * global_server_handle; + +#define CHANGEPW_SERVICE(rqstp) \ + (cmp_gss_names(acceptor_name(rqstp->rq_svccred), gss_changepw_name) |\ + (gss_oldchangepw_name && \ + cmp_gss_names(acceptor_name(rqstp->rq_svccred), \ + gss_oldchangepw_name))) + +int cmp_gss_names(gss_name_t n1, gss_name_t n2) +{ + OM_uint32 emaj, emin; + int equal; + + if (GSS_ERROR(emaj = gss_compare_name(&emin, n1, n2, &equal))) + return(0); + + return(equal); +} + +/* + * Function check_handle + * + * Purpose: Check a server handle and return a com_err code if it is + * invalid or 0 if it is valid. + * + * Arguments: + * + * handle The server handle. + */ + +static int check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} + +/* + * Function: new_server_handle + * + * Purpose: Constructs a server handle suitable for passing into the + * server library API functions, by folding the client's API version + * and calling principal into the server handle returned by + * kadm5_init. + * + * Arguments: + * api_version (input) The API version specified by the client + * rqstp (input) The RPC request + * handle (output) The returned handle + * (output) An error code, or 0 if no error occurred + * + * Effects: + * Returns a pointer to allocated storage containing the server + * handle. If an error occurs, then no allocated storage is + * returned, and the return value of the function will be a + * non-zero com_err code. + * + * The allocated storage for the handle should be freed with + * free_server_handle (see below) when it is no longer needed. + */ + +static kadm5_ret_t new_server_handle(krb5_ui_4 api_version, + struct svc_req *rqstp, + kadm5_server_handle_t + *out_handle) +{ + kadm5_server_handle_t handle; + + if (! (handle = (kadm5_server_handle_t) + malloc(sizeof(*handle)))) + return ENOMEM; + + *handle = *(kadm5_server_handle_t)global_server_handle; + handle->api_version = api_version; + + if (! gss_to_krb5_name(handle, rqstp->rq_clntcred, + &handle->current_caller)) { + free(handle); + return KADM5_FAILURE; + } + + *out_handle = handle; + return 0; +} + +/* + * Function: free_server_handle + * + * Purpose: Free handle memory allocated by new_server_handle + * + * Arguments: + * handle (input/output) The handle to free + */ +static void free_server_handle(kadm5_server_handle_t handle) +{ + krb5_free_principal(handle->context, handle->current_caller); + free(handle); +} + +/* + * Function: setup_gss_names + * + * Purpose: Create printable representations of the client and server + * names. + * + * Arguments: + * rqstp (r) the RPC request + * client_name (w) the gss_buffer_t for the client name + * server_name (w) the gss_buffer_t for the server name + * + * Effects: + * + * Unparses the client and server names into client_name and + * server_name, both of which must be freed by the caller. Returns 0 + * on success and -1 on failure. + */ +int setup_gss_names(struct svc_req *rqstp, + gss_buffer_desc *client_name, + gss_buffer_desc *server_name) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t server_gss_name; + + if (gss_name_to_string(rqstp->rq_clntcred, client_name) != 0) + return -1; + maj_stat = gss_inquire_context(&min_stat, rqstp->rq_svccred, NULL, + &server_gss_name, NULL, NULL, NULL, + NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) { + gss_release_buffer(&min_stat, client_name); + return -1; + } + if (gss_name_to_string(server_gss_name, server_name) != 0) { + gss_release_buffer(&min_stat, client_name); + return -1; + } + return 0; +} + +gss_name_t acceptor_name(gss_ctx_id_t context) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t name; + + maj_stat = gss_inquire_context(&min_stat, context, NULL, &name, + NULL, NULL, NULL, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) + return NULL; + return name; +} + +int cmp_gss_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal princ) +{ + krb5_principal princ2; + int stat; + + if (! gss_to_krb5_name(handle, gss_name, &princ2)) + return 0; + stat = krb5_principal_compare(handle->context, princ, princ2); + krb5_free_principal(handle->context, princ2); + return stat; +} + +int gss_to_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal *princ) +{ + OM_uint32 stat, minor_stat; + gss_buffer_desc gss_str; + gss_OID gss_type; + int success; + + stat = gss_display_name(&minor_stat, gss_name, &gss_str, &gss_type); + if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) + return 0; + success = (krb5_parse_name(handle->context, gss_str.value, princ) == 0); + gss_release_buffer(&minor_stat, &gss_str); + return success; +} +int +gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str) +{ + OM_uint32 stat, minor_stat; + gss_OID gss_type; + int ret; + + stat = gss_display_name(&minor_stat, gss_name, str, &gss_type); + if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) + return 1; + return 0; +} + +generic_ret * +create_principal_1(cprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_ADD, + arg->rec.principal)) { + ret.code = KADM5_AUTH_ADD; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_create_principal((void *)handle, + &arg->rec, arg->mask, + arg->passwd); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_principal", + prime_arg,((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +delete_principal_1(dprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_DELETE, + arg->princ)) { + ret.code = KADM5_AUTH_DELETE; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_delete_principal((void *)handle, arg->princ); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_principal", prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free(prime_arg); + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +modify_principal_1(mprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_MODIFY, + arg->rec.principal)) { + ret.code = KADM5_AUTH_MODIFY; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_modify_principal((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +rename_principal_1(rprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg1, + *prime_arg2; + char prime_arg[BUFSIZ]; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->src, &prime_arg1); + krb5_unparse_name(handle->context, arg->dest, &prime_arg2); + sprintf(prime_arg, "%s to %s", prime_arg1, prime_arg2); + + ret.code = KADM5_OK; + if (! CHANGEPW_SERVICE(rqstp)) { + if (!acl_check(handle->context, rqstp->rq_clntcred, + ACL_DELETE, arg->src)) + ret.code = KADM5_AUTH_DELETE; + if (!acl_check(handle->context, rqstp->rq_clntcred, + ACL_ADD, arg->dest)) { + if (ret.code == KADM5_AUTH_DELETE) + ret.code = KADM5_AUTH_INSUFFICIENT; + else + ret.code = KADM5_AUTH_ADD; + } + } else + ret.code = KADM5_AUTH_INSUFFICIENT; + if (ret.code != KADM5_OK) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_rename_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_rename_principal((void *)handle, arg->src, + arg->dest); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_rename_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg1); + free(prime_arg2); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gprinc_ret * +get_principal_1(gprinc_arg *arg, struct svc_req *rqstp) +{ + static gprinc_ret ret; + kadm5_principal_ent_t_v1 e; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gprinc_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_get_principal (V1)" : "kadm5_get_principal"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (! cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ) && + (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_INQUIRE, + arg->princ))) { + ret.code = KADM5_AUTH_GET; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + if (handle->api_version == KADM5_API_VERSION_1) { + ret.code = kadm5_get_principal_v1((void *)handle, + arg->princ, &e); + if(ret.code == KADM5_OK) { + memcpy(&ret.rec, e, sizeof(kadm5_principal_ent_rec_v1)); + free(e); + } + } else { + ret.code = kadm5_get_principal((void *)handle, + arg->princ, &ret.rec, + arg->mask); + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gprincs_ret * +get_princs_1(gprincs_arg *arg, struct svc_req *rqstp) +{ + static gprincs_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gprincs_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_LIST, + NULL)) { + ret.code = KADM5_AUTH_LIST; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_principals", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_get_principals((void *)handle, + arg->exp, &ret.princs, + &ret.count); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_principals", + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +chpass_principal_1(chpass_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) { + ret.code = chpass_principal_wrapper((void *)handle, arg->princ, + arg->pass); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + acl_check(handle->context, rqstp->rq_clntcred, + ACL_CHANGEPW, arg->princ)) { + ret.code = kadm5_chpass_principal((void *)handle, arg->princ, + arg->pass); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_chpass_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_CHANGEPW; + } + + if(ret.code != KADM5_AUTH_CHANGEPW) { + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_chpass_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +chrand_ret * +chrand_principal_1(chrand_arg *arg, struct svc_req *rqstp) +{ + static chrand_ret ret; + krb5_keyblock *k; + int nkeys; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_chrand_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_randkey_principal (V1)" : "kadm5_randkey_principal"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) { + ret.code = randkey_principal_wrapper((void *)handle, + arg->princ, &k, &nkeys); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + acl_check(handle->context, rqstp->rq_clntcred, + ACL_CHANGEPW, arg->princ)) { + ret.code = kadm5_randkey_principal((void *)handle, arg->princ, + &k, &nkeys); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_CHANGEPW; + } + + if(ret.code == KADM5_OK) { + if (handle->api_version == KADM5_API_VERSION_1) { + krb5_copy_keyblock_contents(handle->context, k, &ret.key); + krb5_free_keyblock(handle->context, k); + } else { + ret.keys = k; + ret.n_keys = nkeys; + } + } + + if(ret.code != KADM5_AUTH_CHANGEPW) { + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +create_policy_1(cpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_ADD, NULL)) { + ret.code = KADM5_AUTH_ADD; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + + } else { + ret.code = kadm5_create_policy((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +delete_policy_1(dpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->name; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_DELETE, NULL)) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_DELETE; + } else { + ret.code = kadm5_delete_policy((void *)handle, arg->name); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +modify_policy_1(mpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_MODIFY, NULL)) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_MODIFY; + } else { + ret.code = kadm5_modify_policy((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gpol_ret * +get_policy_1(gpol_arg *arg, struct svc_req *rqstp) +{ + static gpol_ret ret; + kadm5_ret_t ret2; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_policy_ent_t e; + kadm5_principal_ent_rec caller_ent; + krb5_principal caller; + kadm5_server_handle_t handle; + + xdr_free(xdr_gpol_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_get_policy (V1)" : "kadm5_get_policy"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->name; + + ret.code = KADM5_AUTH_GET; + if (!CHANGEPW_SERVICE(rqstp) && acl_check(handle->context, + rqstp->rq_clntcred, + ACL_INQUIRE, NULL)) + ret.code = KADM5_OK; + else { + ret.code = kadm5_get_principal(handle->lhandle, + handle->current_caller, + &caller_ent, + KADM5_PRINCIPAL_NORMAL_MASK); + if (ret.code == KADM5_OK) { + if (caller_ent.aux_attributes & KADM5_POLICY && + strcmp(caller_ent.policy, arg->name) == 0) { + ret.code = KADM5_OK; + } else ret.code = KADM5_AUTH_GET; + ret2 = kadm5_free_principal_ent(handle->lhandle, + &caller_ent); + ret.code = ret.code ? ret.code : ret2; + } + } + + if (ret.code == KADM5_OK) { + if (handle->api_version == KADM5_API_VERSION_1) { + ret.code = kadm5_get_policy_v1((void *)handle, arg->name, &e); + if(ret.code == KADM5_OK) { + memcpy(&ret.rec, e, sizeof(kadm5_policy_ent_rec)); + free(e); + } + } else { + ret.code = kadm5_get_policy((void *)handle, arg->name, + &ret.rec); + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; + +} + +gpols_ret * +get_pols_1(gpols_arg *arg, struct svc_req *rqstp) +{ + static gpols_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gpols_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_LIST, NULL)) { + ret.code = KADM5_AUTH_LIST; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_policies", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_get_policies((void *)handle, + arg->exp, &ret.pols, + &ret.count); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_policies", + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +getprivs_ret * get_privs_1(krb5_ui_4 *arg, struct svc_req *rqstp) +{ + static getprivs_ret ret; + char *prime_arg; + gss_buffer_desc client_name, service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_getprivs_ret, &ret); + + if (ret.code = new_server_handle(*arg, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + + ret.code = kadm5_get_privs((void *)handle, &ret.privs); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_privs", + client_name.value, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret *init_1(krb5_ui_4 *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + gss_buffer_desc client_name, + service_name; + kadm5_server_handle_t handle; + OM_uint32 minor_stat; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(*arg, rqstp, &handle)) + return &ret; + if (! (ret.code = check_handle((void *)handle))) { + ret.api_version = handle->api_version; + } + + free_server_handle(handle); + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, + (ret.api_version == KADM5_API_VERSION_1 ? + "kadm5_init (V1)" : "kadm5_init"), + client_name.value, + (ret.code == 0) ? "success" : error_message(ret.code), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + + return(&ret); +} diff --git a/src/kadmin/testing/Makefile.ov b/src/kadmin/testing/Makefile.ov new file mode 100644 index 000000000..206bd8551 --- /dev/null +++ b/src/kadmin/testing/Makefile.ov @@ -0,0 +1,8 @@ +# $Id$ + +TOP = .. +include $(TOP)/config.mk/template + +SUBDIRS=util scripts + +expand SubdirTarget diff --git a/src/kadmin/testing/proto/ChangeLog b/src/kadmin/testing/proto/ChangeLog new file mode 100644 index 000000000..71959a3b0 --- /dev/null +++ b/src/kadmin/testing/proto/ChangeLog @@ -0,0 +1,9 @@ +Mon Jul 15 17:11:35 1996 Marc Horowitz + + * krb5.conf.proto: specify a default_keytab_name in /krb5 + +Fri Jul 12 14:46:17 1996 Marc Horowitz + + * kdc.conf.proto: put the stash file in /krb5, so that the root + dir does not need to be writeable. also, the admin system + requires a reference in the conf file to admin_keytab diff --git a/src/kadmin/testing/proto/kdc.conf.proto b/src/kadmin/testing/proto/kdc.conf.proto new file mode 100644 index 000000000..798d6c51b --- /dev/null +++ b/src/kadmin/testing/proto/kdc.conf.proto @@ -0,0 +1,20 @@ +[kdcdefaults] + kdc_ports = 1750 + +[realms] + __REALM__ = { + profile = /krb5/krb5.conf + database_name = /krb5/kdb5 + admin_database_name = /krb5/kadb5 + admin_database_lockfile = /krb5/ovsec_adm.lock + admin_keytab = /krb5/ovsec_adm.srvtab + key_stash_file = /krb5/.k5.__REALM__ + acl_file = /krb5/ovsec_adm.acl + dict_file = /krb5/ovsec_adm.dict + kadmind_port = 1751 + max_life = 10h 0m 0s + max_renewable_life = 7d 0h 0m 0s + master_key_type = des-cbc-crc + supported_enctypes = des-cbc-crc:normal des-cbc-crc:v4 + } + diff --git a/src/kadmin/testing/proto/krb5.conf.proto b/src/kadmin/testing/proto/krb5.conf.proto new file mode 100644 index 000000000..6e0c4687d --- /dev/null +++ b/src/kadmin/testing/proto/krb5.conf.proto @@ -0,0 +1,17 @@ +[libdefaults] + default_realm = __REALM__ + default_keytab_name = FILE:/krb5/v5srvtab + +[realms] + __REALM__ = { + kdc = localhost:1750 + admin_server = localhost:1751 + } + +[domain_realm] + localhost = __REALM__ + +[logging] + admin_server = SYSLOG=ERR:LOCAL6 + kdc = SYSLOG=ERR:LOCAL6 + default = SYSLOG=ERR:LOCAL6 diff --git a/src/kadmin/testing/proto/ovsec_adm.dict b/src/kadmin/testing/proto/ovsec_adm.dict new file mode 100644 index 000000000..b54e3a85e --- /dev/null +++ b/src/kadmin/testing/proto/ovsec_adm.dict @@ -0,0 +1,3 @@ +Abyssinia +Discordianism +foo diff --git a/src/kadmin/testing/scripts/ChangeLog b/src/kadmin/testing/scripts/ChangeLog new file mode 100644 index 000000000..5d7069186 --- /dev/null +++ b/src/kadmin/testing/scripts/ChangeLog @@ -0,0 +1,15 @@ +Fri Jul 12 14:48:20 1996 Marc Horowitz + + * stop_servers_local (true, false): use the path to find these, + instead of looking in /bin explicitly. + + * start_servers_local (/usr/tmp): /usr/tmp doesn't exist on some + systems. Check for that and /var/tmp, and use the one which + exists. (true, false): use the path to find these, instead of + looking in /bin explicitly. + + * make-host-keytab.pl.in: perl5 requires that @ in strings be + backwhacked. (EDIT_KEYTAB): ovsec_adm_keytab is now kadm5_keytab. + + * init_db: kadmin_create should be kdb5_create + diff --git a/src/kadmin/testing/scripts/Makefile.ov b/src/kadmin/testing/scripts/Makefile.ov new file mode 100644 index 000000000..335b636e7 --- /dev/null +++ b/src/kadmin/testing/scripts/Makefile.ov @@ -0,0 +1,19 @@ +# $Id$ + +TOP = ../.. +include $(TOP)/config.mk/template + +GEN_SCRIPTS = compare_dump.pl fixup-conf-files.pl make-host-keytab.pl \ + simple_dump.pl verify_xrunner_report.pl + +all:: $(GEN_SCRIPTS) + +%.pl: %.pl.in + -rm -f $@.tmp + echo "#!$(PERL)" > $@.tmp + sed 1d $@.in >> $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +clean:: + -rm -f $(GEN_SCRIPTS) *.tmp diff --git a/src/kadmin/testing/scripts/compare_dump.pl.in b/src/kadmin/testing/scripts/compare_dump.pl.in new file mode 100644 index 000000000..df93df4a0 --- /dev/null +++ b/src/kadmin/testing/scripts/compare_dump.pl.in @@ -0,0 +1,242 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +# $debug = 1; + +sub usage { die "usage: $0 before after changes\n";} + +sub unique { + local(@list) = @_; + local(%ary); + + print "unique? ",join(" ",@list),"\n" if $debug; + + foreach (@list) { + return(0) if $ary{$_}++; + } + + 1; +} + +$before = shift(@ARGV) || &usage; +$debug++ if $before =~ /^-d/; +$before = shift(@ARGV) || &usage if $debug; +$after = shift(@ARGV) || &usage; +$changes = shift(@ARGV) || &usage; +@ARGV && &usage; + +%policy = + ( + "FIRST",2, + "pw_min_life",2, + "pw_max_life",3, + "pw_min_length",4, + "pw_min_classes",5, + "pw_history_num",6, + "policy_refcnt",7, + "LAST",7, + ); + +%princ = + ( + "FIRST",2, + "kvno",2, + "mod_name",3, + "max_life",4, + "princ_expire_time",5, + "expiration",5, + "pw_expiration",6, + "attributes",7, + "policy",8, + "aux_attributes",9, + "LAST",9, + ); + +%keytab = + ( + "LAST",-1, + ); + +sub re { # @_ = ($cnt, $line) + local($cnt, $line) = @_; + local(@fields) = split(' ',$line); + + @list = ('\S+') x $cnt; + for $f (@fields[3..$#fields]) { + ($f =~ /=/) || die "Bad field: $f in $_"; + if (!defined($this{$`})) { die "Bad parameter $` in $_"; } + + if (($list[$this{$`}] = $') eq '\S+') { + $list[$this{$`}] = '[^\s]+'; + } + } + + join('\s+',@list)."\$"; +} + +open(CHANGES, $changes) || die "Couldn't open $changes: $!\n"; + +while() { + next if s/^\s*\#\#\!\s*\#//; + next if !s/^\s*\#\#\!\s*//; + + split; + + if ($_[1] =~ /princ/) { + %this = %princ; + $this = "princ"; + } elsif ($_[1] =~ /policy/) { + %this = %policy; + $this = "policy"; + } elsif ($_[1] =~ /keytab/) { + %this = %keytab; + $this = $_[1]; + } else { + die "Bad line: $_"; + } + + $cnt = $this{"LAST"}+1; + + if ($_[0] =~ /add/) { + $diff{"+$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /delete/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changefrom/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changeto/) { + $ndiff{"-$this\t$_[2]"} = &re($cnt,$_); + } else { + die "Bad line: $_"; + } +} + +close(CHANGES); + +if ($debug) { + for (keys %diff) { + print " %diff: \"$_\" /$diff{$_}/\n"; + } + + for (keys %ndiff) { + print "%ndiff: \"$_\" /$ndiff{$_}/\n"; + } + + print "\n"; +} + +open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n"; + +$warnings = 0; + +while() { + next if /^\+{3}/; + next if /^\-{3}/; + next if /^@@/; + + print "LINE: $_" if $debug; + + split; + + $key = "$_[0]\t$_[1]"; + $re = $diff{$key}; + + delete $diff{$key}; + + print "%diff: \"$key\" /$re/\n" if $debug; + + if (!$re) { + warn "Unexpected: \"$key\"\n"; + $warnings++; + next; + } + + if (!/$re/) { + warn "Failed: $key\n"; + $warnings++; + next; + } + + if ($new = $ndiff{$key}) { + delete $ndiff{$key}; + + @new = split(/\\s\+/, $new); + for ($i=1;$i<@new;$i++) { + print "NEW: $new[$i]\n" if $debug; + + if ($new[$i] ne '\S+') { + $_[$i] = $new[$i]; + } + } + $_[0] =~ s/^\-//; + $key =~ s/^\-/\+/; + + $diff{$key} = join("\t",@_); + } +} + +close(DIFF); + +open(BEFORE, $before) || die "Couldn't open $before: $!\n"; + +while() { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(BEFORE); + +open(AFTER, $after) || die "Couldn't open $after: $!\n"; + +while() { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(AFTER); + +for (keys %diff) { + warn "Unseen: \"$_\" /$diff{$_}/\n"; + $warnings++; +} + +for (keys %ndiff) { + warn "Unseen changes: \"$_\" /$ndiff{$_}/\n"; + $warnings++; +} + +for (keys %ktkeys) { + if (!&unique(split(' ',$ktkeys{$_}))) { + warn "Some keys not unique for $_\n"; + $warnings++; + } +} + +for (keys %kttimes) { + if (!&unique(split(' ',$kttimes{$_}))) { + warn "Some timestamps not unique for $_\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/compare_dump.plin b/src/kadmin/testing/scripts/compare_dump.plin new file mode 100644 index 000000000..df93df4a0 --- /dev/null +++ b/src/kadmin/testing/scripts/compare_dump.plin @@ -0,0 +1,242 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +# $debug = 1; + +sub usage { die "usage: $0 before after changes\n";} + +sub unique { + local(@list) = @_; + local(%ary); + + print "unique? ",join(" ",@list),"\n" if $debug; + + foreach (@list) { + return(0) if $ary{$_}++; + } + + 1; +} + +$before = shift(@ARGV) || &usage; +$debug++ if $before =~ /^-d/; +$before = shift(@ARGV) || &usage if $debug; +$after = shift(@ARGV) || &usage; +$changes = shift(@ARGV) || &usage; +@ARGV && &usage; + +%policy = + ( + "FIRST",2, + "pw_min_life",2, + "pw_max_life",3, + "pw_min_length",4, + "pw_min_classes",5, + "pw_history_num",6, + "policy_refcnt",7, + "LAST",7, + ); + +%princ = + ( + "FIRST",2, + "kvno",2, + "mod_name",3, + "max_life",4, + "princ_expire_time",5, + "expiration",5, + "pw_expiration",6, + "attributes",7, + "policy",8, + "aux_attributes",9, + "LAST",9, + ); + +%keytab = + ( + "LAST",-1, + ); + +sub re { # @_ = ($cnt, $line) + local($cnt, $line) = @_; + local(@fields) = split(' ',$line); + + @list = ('\S+') x $cnt; + for $f (@fields[3..$#fields]) { + ($f =~ /=/) || die "Bad field: $f in $_"; + if (!defined($this{$`})) { die "Bad parameter $` in $_"; } + + if (($list[$this{$`}] = $') eq '\S+') { + $list[$this{$`}] = '[^\s]+'; + } + } + + join('\s+',@list)."\$"; +} + +open(CHANGES, $changes) || die "Couldn't open $changes: $!\n"; + +while() { + next if s/^\s*\#\#\!\s*\#//; + next if !s/^\s*\#\#\!\s*//; + + split; + + if ($_[1] =~ /princ/) { + %this = %princ; + $this = "princ"; + } elsif ($_[1] =~ /policy/) { + %this = %policy; + $this = "policy"; + } elsif ($_[1] =~ /keytab/) { + %this = %keytab; + $this = $_[1]; + } else { + die "Bad line: $_"; + } + + $cnt = $this{"LAST"}+1; + + if ($_[0] =~ /add/) { + $diff{"+$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /delete/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changefrom/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changeto/) { + $ndiff{"-$this\t$_[2]"} = &re($cnt,$_); + } else { + die "Bad line: $_"; + } +} + +close(CHANGES); + +if ($debug) { + for (keys %diff) { + print " %diff: \"$_\" /$diff{$_}/\n"; + } + + for (keys %ndiff) { + print "%ndiff: \"$_\" /$ndiff{$_}/\n"; + } + + print "\n"; +} + +open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n"; + +$warnings = 0; + +while() { + next if /^\+{3}/; + next if /^\-{3}/; + next if /^@@/; + + print "LINE: $_" if $debug; + + split; + + $key = "$_[0]\t$_[1]"; + $re = $diff{$key}; + + delete $diff{$key}; + + print "%diff: \"$key\" /$re/\n" if $debug; + + if (!$re) { + warn "Unexpected: \"$key\"\n"; + $warnings++; + next; + } + + if (!/$re/) { + warn "Failed: $key\n"; + $warnings++; + next; + } + + if ($new = $ndiff{$key}) { + delete $ndiff{$key}; + + @new = split(/\\s\+/, $new); + for ($i=1;$i<@new;$i++) { + print "NEW: $new[$i]\n" if $debug; + + if ($new[$i] ne '\S+') { + $_[$i] = $new[$i]; + } + } + $_[0] =~ s/^\-//; + $key =~ s/^\-/\+/; + + $diff{$key} = join("\t",@_); + } +} + +close(DIFF); + +open(BEFORE, $before) || die "Couldn't open $before: $!\n"; + +while() { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(BEFORE); + +open(AFTER, $after) || die "Couldn't open $after: $!\n"; + +while() { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(AFTER); + +for (keys %diff) { + warn "Unseen: \"$_\" /$diff{$_}/\n"; + $warnings++; +} + +for (keys %ndiff) { + warn "Unseen changes: \"$_\" /$ndiff{$_}/\n"; + $warnings++; +} + +for (keys %ktkeys) { + if (!&unique(split(' ',$ktkeys{$_}))) { + warn "Some keys not unique for $_\n"; + $warnings++; + } +} + +for (keys %kttimes) { + if (!&unique(split(' ',$kttimes{$_}))) { + warn "Some timestamps not unique for $_\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/find-make.sh b/src/kadmin/testing/scripts/find-make.sh new file mode 100644 index 000000000..904730dfa --- /dev/null +++ b/src/kadmin/testing/scripts/find-make.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +POSSIBILITIES=' +/usr/local/bin/gmake +/usr/local/bin/make +' + +for file in $POSSIBILITIES; do + if [ -f $file ]; then + echo $file + exit 0 + fi +done + +echo gmake +echo '$0 could not find make!' 1>&2 +exit 1 + diff --git a/src/kadmin/testing/scripts/fixup-conf-files.pl.in b/src/kadmin/testing/scripts/fixup-conf-files.pl.in new file mode 100644 index 000000000..d7834d1c7 --- /dev/null +++ b/src/kadmin/testing/scripts/fixup-conf-files.pl.in @@ -0,0 +1,344 @@ +#!/usr/local/bin/perl +# +# Usage: fixup-conf-files.pl [-server hostname] + +$verbose = $ENV{'VERBOSE_TEST'}; +$archos = $ENV{'ARCH_OS'}; + +$REALM = "SECURE-TEST.OV.COM"; + +sub replace { + local($old, $new, $backup) = @_; + local($dev, $ino, $mode); + + $new = $old.".new" if !$new; + $backup = $old.".bak" if !$backup; + + chmod($mode,$new) if (($dev, $ino, $mode) = stat($old)); + + unlink($backup); + link($old, $backup) || die "couldn't make backup link: $backup: $!\n" + if -e $old; + rename($new, $old) || die "couldn't rename $old to $new: $!\n"; +} + +if (@ARGV == 2 && $ARGV[0] eq "-server") { + $servername = $ARGV[1]; +} elsif (@ARGV != 0) { + print STDERR "Usage: $0 fixup-conf-files.pl [-server hostname]\n"; +} + +sub canonicalize_name { + local($hostname) = @_; + local($d, $addr, $addrtype); + + ($host,$d,$addrtype,$d,$addr) = gethostbyname($hostname); + die "couldn't get hostname $hostname\n" if !$host; + ($host) = gethostbyaddr($addr,$addrtype); + die "couldn't reverse-resolve $hostname\n" if !$host; + return $host; +} + +## Get server's canonical hostname. +if ($servername) { + $serverhost = $servername; +} else { + chop ($serverhost = `hostname`); +} +$serverhost = &canonicalize_name($serverhost); + +## Get local canonical hostname +chop($localhost=`hostname`); +$localhost = &canonicalize_name($localhost); + +## parse krb.conf + +if (open(KCONF, "/etc/athena/krb.conf")) { + chop($hrealm = ); + + $confok = 0; + + while() { + $confs .= $_ if !/^$REALM\s+/o; + $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi; + } + + close(KCONF); +} + +## rewrite krb.conf if necessary. + +if (($hrealm ne $REALM) || !$confok) { + print "Rewriting /etc/athena/krb.conf...\n" if $verbose; + + open(KCONF, ">/etc/athena/krb.conf.new") || + die "couldn't open /etc/athena/krb.conf.new: $!\n"; + + print KCONF "$REALM\n"; + print KCONF "$REALM $serverhost admin server\n"; + print KCONF $confs; + + close(KCONF); + + &replace("/etc/athena/krb.conf"); +} + +## parse krb.realms + +if (open(KREALMS, "/etc/athena/krb.realms")) { + $serverrealmok = 0; + $localrealmok = 0; + + while() { + $realms .= $_ + if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi; + $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi; + $localrealmok = 1 if /^$localhost\s+$REALM$/oi; + } + + close(KREALMS); +} + +## rewrite krb.realms if necessary. + +if (!$serverrealmok || !$localrealmok) { + print "Rewriting /etc/athean/krb.realms...\n" if $verbose; + + open(KREALMS, ">/etc/athena/krb.realms.new") || + die "couldn't open /etc/athena/krb.realms.new: $!\n"; + + print KREALMS "$serverhost $REALM\n"; + print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost); + print KREALMS $realms; + + close(KREALMS); + + &replace("/etc/athena/krb.realms"); +} + +# ## read /etc/passwd +# +# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n"; +# +# $passok = 0; +# +# if ($archos ne "solaris2.3") { +# %mypass = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } else { +# %mypass = +# ( +# "root", "x", +# "testenc", "x", +# "testuser", "x", +# "pol1", "x", +# "pol2", "x", +# "pol3", "x", +# ); +# %myshadow = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } +# +# $chpw = 0; +# +# while() { +# if (/^([^:]+):([^:]+):/ && $mypass{$1}) { +# $users{$1}++; +# if ($2 ne $mypass{$1}) { +# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/; +# $chpw++; +# } +# } +# $pass .= $_; +# } +# +# $passok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n"; +# $passok = 0; +# } +# } +# close(PASSWD); +# +# ## rewrite passwd if necessary. +# +# if ($chpw || !$passok) { +# print "Rewriting /etc/passwd...\n" if $verbose; +# +# open(PASSWD, ">/etc/passwd.new") || +# die "couldn't open /etc/passwd.new: $!\n"; +# +# print PASSWD $pass; +# +# close(PASSWD); +# +# &replace("/etc/passwd"); +# } +# +# if ($archos eq "solaris2.3") { +# +# ## read /etc/shadow +# +# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n"; +# +# $shadowok = 0; +# $chpw = 0; +# %users = (); +# +# while() { +# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) { +# $users{$1}++; +# if ($2 ne $myshadow{$1}) { +# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/; +# $chpw++; +# } +# } +# $shadow .= $_; +# } +# +# $shadowok = 1; +# +# for (keys %myshadow) { +# if (!$users{$_}) { +# $shadow .= "$_:$myshadow{$_}:6445::::::\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if ($chpw || !$shadowok) { +# print "Rewriting /etc/shadow...\n" if $verbose; +# +# open(SHADOW, ">/etc/shadow.new") || +# die "couldn't open /etc/shadow.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/shadow"); +# } +# } +# +# if ($archos eq "aix3.2") { +# +# ## read /etc/security/passwd +# +# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n"; +# +# $shadowok = 0; +# %users = (); +# +# while() { +# if (/^([^:]+):\s*$/ && $mypass{$1}) { +# $user = $1; +# $users{$user}++; +# # arrange for the user to have a password entry and none other +# while () { +# last if (!/=/); +# } +# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n"; +# } else { +# $shadow .= $_; +# } +# } +# +# $shadowok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if (!$shadowok) { +# print "Rewriting /etc/security/passwd...\n" if $verbose; +# +# open(SHADOW, ">/etc/security/passwd.new") || +# die "couldn't open /etc/security/passwd.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/security/passwd"); +# } +# } +# +# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n"; +# open(NEW_SERVICES, ">/etc/services.new") || +# die "couldn't open /etc/services.new: $!\n"; +# +# print "Rewriting /etc/services...\n" if $verbose; +# +# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec', +# 'kerberos5', 'kerberos4', 'kerberos_master', +# 'passwd_server', 'eklogin', 'krb5_prop', +# 'kerberos_adm', 'kerberos-adm'); +# for (@needed_services) { +# $needed_services{$_}++; +# } +# +# while () { +# m/^\s*([^\#\s][^\s]+)/; +# if ($needed_services{$1}) { +# print "+ Commenting out old entry: $1\n" if $verbose; +# print NEW_SERVICES "# $_"; +# } else { +# print NEW_SERVICES $_; +# } +# } +# +# close(SERVICES); +# +# print NEW_SERVICES <); + + $confok = 0; + + while() { + $confs .= $_ if !/^$REALM\s+/o; + $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi; + } + + close(KCONF); +} + +## rewrite krb.conf if necessary. + +if (($hrealm ne $REALM) || !$confok) { + print "Rewriting /etc/athena/krb.conf...\n" if $verbose; + + open(KCONF, ">/etc/athena/krb.conf.new") || + die "couldn't open /etc/athena/krb.conf.new: $!\n"; + + print KCONF "$REALM\n"; + print KCONF "$REALM $serverhost admin server\n"; + print KCONF $confs; + + close(KCONF); + + &replace("/etc/athena/krb.conf"); +} + +## parse krb.realms + +if (open(KREALMS, "/etc/athena/krb.realms")) { + $serverrealmok = 0; + $localrealmok = 0; + + while() { + $realms .= $_ + if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi; + $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi; + $localrealmok = 1 if /^$localhost\s+$REALM$/oi; + } + + close(KREALMS); +} + +## rewrite krb.realms if necessary. + +if (!$serverrealmok || !$localrealmok) { + print "Rewriting /etc/athean/krb.realms...\n" if $verbose; + + open(KREALMS, ">/etc/athena/krb.realms.new") || + die "couldn't open /etc/athena/krb.realms.new: $!\n"; + + print KREALMS "$serverhost $REALM\n"; + print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost); + print KREALMS $realms; + + close(KREALMS); + + &replace("/etc/athena/krb.realms"); +} + +# ## read /etc/passwd +# +# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n"; +# +# $passok = 0; +# +# if ($archos ne "solaris2.3") { +# %mypass = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } else { +# %mypass = +# ( +# "root", "x", +# "testenc", "x", +# "testuser", "x", +# "pol1", "x", +# "pol2", "x", +# "pol3", "x", +# ); +# %myshadow = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } +# +# $chpw = 0; +# +# while() { +# if (/^([^:]+):([^:]+):/ && $mypass{$1}) { +# $users{$1}++; +# if ($2 ne $mypass{$1}) { +# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/; +# $chpw++; +# } +# } +# $pass .= $_; +# } +# +# $passok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n"; +# $passok = 0; +# } +# } +# close(PASSWD); +# +# ## rewrite passwd if necessary. +# +# if ($chpw || !$passok) { +# print "Rewriting /etc/passwd...\n" if $verbose; +# +# open(PASSWD, ">/etc/passwd.new") || +# die "couldn't open /etc/passwd.new: $!\n"; +# +# print PASSWD $pass; +# +# close(PASSWD); +# +# &replace("/etc/passwd"); +# } +# +# if ($archos eq "solaris2.3") { +# +# ## read /etc/shadow +# +# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n"; +# +# $shadowok = 0; +# $chpw = 0; +# %users = (); +# +# while() { +# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) { +# $users{$1}++; +# if ($2 ne $myshadow{$1}) { +# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/; +# $chpw++; +# } +# } +# $shadow .= $_; +# } +# +# $shadowok = 1; +# +# for (keys %myshadow) { +# if (!$users{$_}) { +# $shadow .= "$_:$myshadow{$_}:6445::::::\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if ($chpw || !$shadowok) { +# print "Rewriting /etc/shadow...\n" if $verbose; +# +# open(SHADOW, ">/etc/shadow.new") || +# die "couldn't open /etc/shadow.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/shadow"); +# } +# } +# +# if ($archos eq "aix3.2") { +# +# ## read /etc/security/passwd +# +# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n"; +# +# $shadowok = 0; +# %users = (); +# +# while() { +# if (/^([^:]+):\s*$/ && $mypass{$1}) { +# $user = $1; +# $users{$user}++; +# # arrange for the user to have a password entry and none other +# while () { +# last if (!/=/); +# } +# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n"; +# } else { +# $shadow .= $_; +# } +# } +# +# $shadowok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if (!$shadowok) { +# print "Rewriting /etc/security/passwd...\n" if $verbose; +# +# open(SHADOW, ">/etc/security/passwd.new") || +# die "couldn't open /etc/security/passwd.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/security/passwd"); +# } +# } +# +# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n"; +# open(NEW_SERVICES, ">/etc/services.new") || +# die "couldn't open /etc/services.new: $!\n"; +# +# print "Rewriting /etc/services...\n" if $verbose; +# +# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec', +# 'kerberos5', 'kerberos4', 'kerberos_master', +# 'passwd_server', 'eklogin', 'krb5_prop', +# 'kerberos_adm', 'kerberos-adm'); +# for (@needed_services) { +# $needed_services{$_}++; +# } +# +# while () { +# m/^\s*([^\#\s][^\s]+)/; +# if ($needed_services{$1}) { +# print "+ Commenting out old entry: $1\n" if $verbose; +# print NEW_SERVICES "# $_"; +# } else { +# print NEW_SERVICES $_; +# } +# } +# +# close(SERVICES); +# +# print NEW_SERVICES <&2 + exit 1 +fi + +IROOT=$TOP/.. +ADMIN=$TOP/create:$IROOT/admin/stash:$IROOT/admin/destroy +BIN=$IROOT/bin +ETC=$IROOT/etc +SBIN=$TOP/keytab:$TOP/server +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM + +DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} + +PATH=$ADMIN:$BIN:$ETC:$SBIN:$PATH; export PATH + +rm -rf /krb5/* +if [ -d /krb5 ]; then + true +else + mkdir /krb5 +fi + +# touch /krb5/syslog +# for pid in `$PS_ALL | awk '/syslogd/ && !/awk/ {print $2}'` ; do +# case "$pid" in +# xxx) ;; +# *) +# if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi +# kill -1 $pid +# ;; +# esac +# done + +sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/krb5.conf.proto > /krb5/krb5.conf +sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/kdc.conf.proto > /krb5/kdc.conf + +kdb5_create -P mrroot -s -r $REALM $REDIRECT + +cp $TESTDIR/proto/ovsec_adm.dict /krb5/ovsec_adm.dict + +eval $SRVTCL <<'EOF' $REDIRECT +source $env(TCLUTIL) +set r $env(REALM) + +set cmds { + {ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle} + + {ovsec_kadm_create_policy $server_handle "test-pol 0 10000 8 2 3 0" \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LENGTH OVSEC_KADM_PW_MIN_CLASSES OVSEC_KADM_PW_MAX_LIFE OVSEC_KADM_PW_HISTORY_NUM}} + {ovsec_kadm_create_policy $server_handle "once-a-min 30 0 0 0 0 0" \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LIFE}} + {ovsec_kadm_create_policy $server_handle "dict-only 0 0 0 0 0 0" \ + {OVSEC_KADM_POLICY}} + {ovsec_kadm_create_policy $server_handle [simple_policy test-pol-nopw] \ + {OVSEC_KADM_POLICY}} + + {ovsec_kadm_create_principal $server_handle \ + [simple_principal testuser@$r] {OVSEC_KADM_PRINCIPAL} notathena} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test1@$r] {OVSEC_KADM_PRINCIPAL} test1} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test2@$r] {OVSEC_KADM_PRINCIPAL} test2} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test3@$r] {OVSEC_KADM_PRINCIPAL} test3} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/modify@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/delete@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/none@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/rename@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/mod-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/mod-delete@$r] {OVSEC_KADM_PRINCIPAL} \ + admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-delete@$r] {OVSEC_KADM_PRINCIPAL} \ + admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-mod@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/no-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/no-delete@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol1@$r test-pol] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol111111} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol2@$r once-a-min] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol222222} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol3@$r dict-only] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol333333} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol admin/get-pol@$r test-pol-nopw] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} StupidAdmin} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol admin/pol@$r test-pol-nopw] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} StupidAdmin} + + {ovsec_kadm_create_principal $server_handle \ + [simple_principal changepw/kerberos] \ + {OVSEC_KADM_PRINCIPAL} {XXX THIS IS WRONG}} + + {ovsec_kadm_destroy $server_handle} +} + +foreach cmd $cmds { + if {[catch $cmd output]} { + puts stderr "Error! Command: $cmd\nError: $output" + exit 1 + } else { + puts stdout $output + } +} +EOF + +if [ $? -ne 0 ]; then + echo "Error in $SRVTCL!" 1>&2 + exit 1 +fi + +cat > /krb5/ovsec_adm.acl < /krb5/setup.csh < $ktfile"; + + $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\""; + + for ($cmd, $cmd2, $cmd3) { + print "$_\n" if ($verbose); + + system($_) && die "Couldn't run $_: $!.\n"; + } +} +else { + $redirect = "> /dev/null" if (! $verbose); + + $cmd = "$EDIT_KEYTAB -k $ktfile"; + $cmd .= " -q" if (! $verbose); + $cmd .= " -a -c"; + for (@princs) { + if (system "$cmd $_") { + sleep(1); + die "Error in system($cmd $_)\n"; + } + } +} + +if (! -f $ktfile) { + die "$ktfile not created.\n"; +} diff --git a/src/kadmin/testing/scripts/make-host-keytab.plin b/src/kadmin/testing/scripts/make-host-keytab.plin new file mode 100644 index 000000000..14d7b10b5 --- /dev/null +++ b/src/kadmin/testing/scripts/make-host-keytab.plin @@ -0,0 +1,138 @@ +#!/usr/local/bin/perl + +$server = undef; +@princs = (); +$top = undef; + +($whoami = $0) =~ s,.*/,,; +$usage = "Usage: $whoami [ -server server ] [ -princ principal ] + [ -top dirname ] [ -verbose ] filename + Server defaults to the local host. + Default principals are host/hostname\@SECURE-TEST.OV.COM and + test/hostname\@SECURE-TEST.OV.COM. + If any principals are specified, the default principals are + not added to the srvtab. + The string \"xCANONHOSTx\" in a principal specification will be + replaced by the canonical host name of the local host."; + +@ORIG_ARGV = @ARGV; + +while (($_ = $ARGV[0]) && /^-/) { + shift; + if (/^-server$/) { + ($server = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-princ$/) { + ($princ = shift) || die "Missing argument to $_ option.\n$usage\n"; + push(@princs, $princ); + } + elsif (/^-top$/) { + ($top = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-verbose$/) { + $verbose++; + } + elsif (/^--$/) { + last; + } + else { + die "Unknown option $_.\n$usage\n"; + } +} + +@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM", + "test/xCANONHOSTx\@SECURE-TEST.OV.COM") + if (! @princs); + +$ktfile = shift(@ARGV) || die "need a keytab file\n"; + +$verbose++ if ($ENV{'VERBOSE_TEST'}); + +print "In $0 @ORIG_ARGV...\n" if ($verbose); + +chop ($canonhost = `hostname`); + +($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost); +die "couldn't get canonical hostname\n" if !($canonhost && @addrs); +($canonhost) = gethostbyaddr($addrs[0],$addrtype); +die "couldn't get canonical hostname\n" if (!$canonhost); + +for (@princs) { + s/xCANONHOSTx/$canonhost/g; +} + +die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n" + if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'})); + +$top = $ENV{'TOP'} if (! $top); +$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing"); +$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami"); +$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/ovsec_kadm_srv_tcl"); +$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t"); +# This'll be wrong sometimes +$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh'); +$EDIT_KEYTAB = ($ENV{'EDIT_KEYTAB'} || "$top/keytab/kadm5_keytab.local"); + +if ($server) { +# XXX Using /usr/ucb/rsh for now. + +# Strip command line options because we're adding our own. + + $MAKE_KEYTAB =~ s/ .*//; + + if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) { +# Replace the old TOP with the new one where necessary + for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { + eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;"; + } + +# Make the paths as short as possible so our command line isn't too long. +# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;"; +# } +# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;"; +# } + } + + $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top "; + $cmd .= "VERBOSE_TEST=$verbose " if ($verbose); + $cmd .= "TESTDIR=$TESTDIR "; + $cmd .= "SRVTCL=$SRVTCL "; + $cmd .= "TCLUTIL=$TCLUTIL "; + + $cmd .= "CMD='$MAKE_KEYTAB "; + for (@princs) { + $cmd .= "-princ $_ "; + } + $cmd .= " /tmp/make-keytab.$canonhost.$$'";#'; + + $cmd = "$RSH_CMD $server -l root -n \"$cmd\""; + + $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile"; + + $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\""; + + for ($cmd, $cmd2, $cmd3) { + print "$_\n" if ($verbose); + + system($_) && die "Couldn't run $_: $!.\n"; + } +} +else { + $redirect = "> /dev/null" if (! $verbose); + + $cmd = "$EDIT_KEYTAB -k $ktfile"; + $cmd .= " -q" if (! $verbose); + $cmd .= " -a -c"; + for (@princs) { + if (system "$cmd $_") { + sleep(1); + die "Error in system($cmd $_)\n"; + } + } +} + +if (! -f $ktfile) { + die "$ktfile not created.\n"; +} diff --git a/src/kadmin/testing/scripts/qualname b/src/kadmin/testing/scripts/qualname new file mode 100644 index 000000000..3d047c550 --- /dev/null +++ b/src/kadmin/testing/scripts/qualname @@ -0,0 +1,18 @@ +#!/afs/athena/contrib/perl/p + +if ($#ARGV == -1) { + chop($hostname = `hostname`); +} else { + $hostname = $ARGV[0]; +} + +if (! (($type,$addr) = (gethostbyname($hostname))[2,4])) { + print STDERR "No such host: $hostname\n"; + exit(1); +} +if (! ($qualname = (gethostbyaddr($addr,$type))[0])) { + print STDERR "No address information for host $hostname\n"; + exit(1); +} +print "$qualname\n"; + diff --git a/src/kadmin/testing/scripts/save_files.sh b/src/kadmin/testing/scripts/save_files.sh new file mode 100644 index 000000000..b9fc37319 --- /dev/null +++ b/src/kadmin/testing/scripts/save_files.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +while [ $# -gt 0 ] ; do + case $1 in + -start_servers) + start_servers=$1 + ;; + esac + shift +done + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +# files="/etc/inetd.conf /etc/syslog.conf /etc/krb.conf \ +# /etc/krb.realms /etc/passwd /etc/services /etc/v5srvtab \ +# /etc/rc.local /etc/shadow /etc/security/passwd /.k5login \ +# /.secure/etc/passwd /etc/athena/inetd.conf" + +files="/etc/krb.conf /etc/krb.realms /etc/athena/krb.conf \ + /etc/athena/krb.realms /etc/v5srvtab" + +name=`basename $0` + +make_dne_name() +{ + dne_name="/tmp/"`echo $1 | sed -e 's,/,#,g'`".did-not-exist" +} + +for f in $files ; do + if [ "$name" = "save_files.sh" ]; then + if [ -f $f.pre-secure ]; then + if $VERBOSE; then + echo "Warning! $f.pre-secure exists, not saving." + fi + elif [ ! -f $f ]; then + make_dne_name $f + cp /dev/null $dne_name + else + cp $f $f.pre-secure + fi + else + make_dne_name $f + if [ -f $dne_name ]; then + rm -f $f $dne_name + elif [ ! -f $f.pre-secure ]; then + if [ "x$start_servers" = "x" ]; then + echo "Warning! $f.pre-secure does not exist!" 1>&2 + fi + else + if cp $f.pre-secure $f; then + rm $f.pre-secure + else + echo "Warning! cp failed!" 1>&2 + fi + fi + fi +done + +# DUMMY=${INETD:=/etc/inetd} +# if $VERBOSE; then +# echo "Killing and restarting $INETD" +# fi +# kill `$PS_ALL | awk '/inetd/ && !/awk/ {print $2}'` +# $INETD diff --git a/src/kadmin/testing/scripts/simple_dump.pl.in b/src/kadmin/testing/scripts/simple_dump.pl.in new file mode 100644 index 000000000..ea94ab2d1 --- /dev/null +++ b/src/kadmin/testing/scripts/simple_dump.pl.in @@ -0,0 +1,88 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +## ovsec_adm_export format +## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt +## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys +$oaevers = "1.0"; + +open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n"; + +open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") || + die "Couldn't get oae: $!\n"; + +$header = ; + +die "Not ovsec_adm_export output\n" + if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected oae version $oaevers, got $stdinvers instead.\n" + if $stdinvers ne $oaevers; + +while() { + if (/^End of Database/) { + last; + } elsif (/^policy/) { + print SORT; + } elsif (/^princ/) { + split(/\t/); + + $_[2] = "\"\"" if !$_[2]; + + $_[3] = hex("0x".$_[3]); + + $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]); + } +} + +## kdb_edit ddb format +## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8; +$ddbvers = "2.0"; + +open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") || + die "Couldn't get ddb: $!\n"; + +$header = ; + +die "Not a kdb5_edit ddb\n" + if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected ddb version $ddbvers, got $stdinvers instead.\n" + if $stdinvers ne $ddbvers; + +## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes + +while() { + split; + + print SORT join("\t","princ",(@_)[6,9,19,10,13,14], + sprintf("0x%04x",$_[21]), + $princ{$_[6]}),"\n"; +} + +close(DDB); + +for $keytab (@ARGV) { + open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") || + die "Couldn't list $keytab: $!\n"; + + $dummy = ; + $dummy = ; + $dummy = ; + + while() { + s/^\s+//; + split; + printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab, + @_[3,0,4,1,2]); + } +} + +close(SORT); diff --git a/src/kadmin/testing/scripts/simple_dump.plin b/src/kadmin/testing/scripts/simple_dump.plin new file mode 100644 index 000000000..ea94ab2d1 --- /dev/null +++ b/src/kadmin/testing/scripts/simple_dump.plin @@ -0,0 +1,88 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +## ovsec_adm_export format +## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt +## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys +$oaevers = "1.0"; + +open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n"; + +open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") || + die "Couldn't get oae: $!\n"; + +$header = ; + +die "Not ovsec_adm_export output\n" + if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected oae version $oaevers, got $stdinvers instead.\n" + if $stdinvers ne $oaevers; + +while() { + if (/^End of Database/) { + last; + } elsif (/^policy/) { + print SORT; + } elsif (/^princ/) { + split(/\t/); + + $_[2] = "\"\"" if !$_[2]; + + $_[3] = hex("0x".$_[3]); + + $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]); + } +} + +## kdb_edit ddb format +## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8; +$ddbvers = "2.0"; + +open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") || + die "Couldn't get ddb: $!\n"; + +$header = ; + +die "Not a kdb5_edit ddb\n" + if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected ddb version $ddbvers, got $stdinvers instead.\n" + if $stdinvers ne $ddbvers; + +## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes + +while() { + split; + + print SORT join("\t","princ",(@_)[6,9,19,10,13,14], + sprintf("0x%04x",$_[21]), + $princ{$_[6]}),"\n"; +} + +close(DDB); + +for $keytab (@ARGV) { + open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") || + die "Couldn't list $keytab: $!\n"; + + $dummy = ; + $dummy = ; + $dummy = ; + + while() { + s/^\s+//; + split; + printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab, + @_[3,0,4,1,2]); + } +} + +close(SORT); diff --git a/src/kadmin/testing/scripts/start_servers b/src/kadmin/testing/scripts/start_servers new file mode 100644 index 000000000..2e395faf8 --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Usage: start_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${START_SERVERS_LOCAL=$TESTDIR/scripts/start_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=/usr/ucb/rsh} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + $SAVE_FILES || exit 1 + $FIX_CONF_FILES -server $hostname || exit 1 + +# Using /usr/ucb/rsh and getting rid of "-k $REALM" until we get +# around to fixing the fact that Kerberos rsh doesn't strip out "-k +# REALM" when falling back. + + START_SERVERS_LOCAL=`echo $START_SERVERS_LOCAL|sed "s%$TOP%$rempath%"` + CMD="$RSH_CMD $hostname -l root -n \ + \"cd $rempath; \\\`testing/scripts/find-make.sh\\\` execute VERBOSE_TEST=$VERBOSE_TEST \ + TOP=$rempath \ + CMD='$START_SERVERS_LOCAL $rempath'\"" + + if $VERBOSE; then + echo "+++" + echo "+++ Begin execution of start_servers_local on $hostname" + echo "+++" + echo $CMD + fi + eval $CMD + if $VERBOSE; then + echo "+++" + echo "+++ End execution of start_servers_local on $hostname" + echo "+++" + fi +else + $START_SERVERS_LOCAL +fi + diff --git a/src/kadmin/testing/scripts/start_servers_local b/src/kadmin/testing/scripts/start_servers_local new file mode 100644 index 000000000..a9c8e7957 --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers_local @@ -0,0 +1,196 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${INITDB=$TESTDIR/scripts/init_db} +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} +DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local} + +if [ -d /usr/tmp ]; then + usrtmp=/usr/tmp +else + usrtmp=/var/tmp +fi + +$STOP_SERVERS_LOCAL -start_servers + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +v4files=false +while :; do + case $1 in + -keysalt) + shift + if [ $# -gt 0 ]; then + keysalts="$keysalts $1" + else + break + fi + ;; + -kdcport) + shift + if [ $# -gt 0 ]; then + kdcport=$1 + else + break + fi + ;; + -v4files) + if [ "`whoami`" != "root" ]; then + echo "You must be root to use -v4files!" 1>&2 + exit 1 + fi + v4files=true + ;; + *) + break + ;; + esac + shift +done + +if [ $# -gt 1 ]; then + echo "Usage: $0 [-kdcport port] [-keysalts tuple] ... [top]" 1>&2 + exit 1 +elif [ $# = 1 ]; then + TOP=$1 + export TOP +fi + +# fixup the system config files +if $v4files; then + $SAVE_FILES || exit 1 + $FIX_CONF_FILES || exit 1 +fi + +# create a fresh db + +$INITDB "$keysalts" || exit 1 + +# Post-process the config files based on our arguments +if [ "$keysalts" != "" ]; then + sedcmd="s/\([ ]*supported_enctypes =\).*/\1 $keysalts/" + sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new + mv /krb5/kdc.conf.new /krb5/kdc.conf +fi +if [ "$kdcport" != "" ] ; then + sedcmd="s/\(kdc_ports = .*\)[ ]*/\1, $kdcport/" + sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new + mv /krb5/kdc.conf.new /krb5/kdc.conf +fi + +# allow admin to krlogin as root (for cleanup) +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM +hostname=`hostname` +QUALNAME=`$TOP/testing/scripts/qualname $hostname`; export QUALNAME + +eval $SRVTCL <<'EOF' $REDIRECT +source $env(TOP)/testing/tcl/util.t +set r $env(REALM) +set q $env(QUALNAME) +puts stdout [ovsec_kadm_init $env(SRVTCL) mrroot null $r \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle] +puts stdout [ovsec_kadm_create_principal $server_handle \ + [simple_principal host/$q@$r] {OVSEC_KADM_PRINCIPAL} notathena] +puts stdout [ovsec_kadm_destroy $server_handle] +EOF + +# rm -f /etc/v5srvtab +# eval $LOCAL_MAKE_KEYTAB -princ host/xCANONHOSTx /etc/v5srvtab $REDIRECT + +# run the servers (from the build tree) + +adm_start_file=/tmp/adm_server_start.$$ +kdc_start_file=/tmp/kdc_server_start.$$ + +rm -f $kdc_start_file + +(trap "" 2; cd $TOP/../kdc; ./krb5kdc; touch $kdc_start_file) \ + < /dev/null > $usrtmp/kdc-log 2>&1 & + +s=10 +max_s=60 +sofar_s=0 +timewait_s=300 + +while true; do + rm -f $adm_start_file + + (sleep 5; cd $TOP/server; ./kadmind $ovadm_args; \ + touch $adm_start_file) < /dev/null > $usrtmp/kadm-log 2>&1 & + + # wait until they start + + while [ $sofar_s -le $max_s ]; do + if $VERBOSE; then + echo "Sleeping for $s seconds to allow servers" \ + "to start..." + fi + + sofar_s=`expr $sofar_s + $s` + + sleep $s + + if [ -f $adm_start_file -a -f $kdc_start_file ]; then + break + fi + + done + + if [ $sofar_s -le $max_s ]; then + if $VERBOSE; then + LOG_USER='log_user 1' + else + LOG_USER='log_user 0' + fi + if expect <&2 + exit 1 + fi +done diff --git a/src/kadmin/testing/scripts/stop_servers b/src/kadmin/testing/scripts/stop_servers new file mode 100644 index 000000000..fc5372dd4 --- /dev/null +++ b/src/kadmin/testing/scripts/stop_servers @@ -0,0 +1,84 @@ +#!/bin/sh +# +# Usage: stop_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=/usr/ucb/rsh} +DUMMY=${RESTORE_FILES=$TESTDIR/scripts/restore_files.sh} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + if $VERBOSE; then + echo "+++ Stopping servers on remote host $hostname..." + fi + +# $FIX_CONF_FILES -server $hostname +# +# KRB5CCNAME=FILE:/tmp/krb5cc_stop_servers; export KRB5CCNAME +# +# expect <) { + if (/Process termination:/ && !/\bOK\b/) { + warn "Process termination not OK\n"; + $warnings++; + } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) { + warn "Number of detected mismatches = $1\n"; + $warnings++; + } elsif (/Detailed Results Description/) { + break; + } +} + +while() { + next if !/^\d+\s+/; + + split; + + if (($_[2] ne "run") && + ($_[2] ne "OK") && + ($_[2] ne "end-of-test")) { + warn "Unexpected result code $_[2] from test $_[4]\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.plin b/src/kadmin/testing/scripts/verify_xrunner_report.plin new file mode 100644 index 000000000..9d83c3ea2 --- /dev/null +++ b/src/kadmin/testing/scripts/verify_xrunner_report.plin @@ -0,0 +1,38 @@ +#!/usr/local/bin/perl + +sub usage { die "usage: $0 reportfile\n"; } + +$report = shift(@ARGV) || die &usage; + +open(REPORT, $report) || die "Couldn't open $report: $!\n"; + +while() { + if (/Process termination:/ && !/\bOK\b/) { + warn "Process termination not OK\n"; + $warnings++; + } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) { + warn "Number of detected mismatches = $1\n"; + $warnings++; + } elsif (/Detailed Results Description/) { + break; + } +} + +while() { + next if !/^\d+\s+/; + + split; + + if (($_[2] ne "run") && + ($_[2] ne "OK") && + ($_[2] ne "end-of-test")) { + warn "Unexpected result code $_[2] from test $_[4]\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/tcl/util.t b/src/kadmin/testing/tcl/util.t new file mode 100644 index 000000000..f4688aeee --- /dev/null +++ b/src/kadmin/testing/tcl/util.t @@ -0,0 +1,61 @@ +proc simple_principal {name} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 null 0" +} + +proc princ_w_pol {name policy} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 {$policy} 0" +} + +proc simple_policy {name} { + return "{$name} 0 0 0 0 0 0" +} + +proc config_params {masks values} { + if {[llength $masks] != [llength $values]} { + error "config_params: length of mask and values differ" + } + + set params [list $masks 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {}] + for {set i 0} {$i < [llength $masks]} {incr i} { + set mask [lindex $masks $i] + set value [lindex $values $i] + switch -glob -- $mask { + "KADM5_CONFIG_REALM" {set params [lreplace $params 1 1 $value]} + "KADM5_CONFIG_PROFILE" {set params [lreplace $params 2 2 $value]} + "KADM5_CONFIG_KADMIND_PORT" { + set params [lreplace $params 3 3 $value]} + "KADM5_CONFIG_ADMIN_SERVER" { + set params [lreplace $params 4 4 $value]} + "KADM5_CONFIG_DBNAME" {set params [lreplace $params 5 5 $value]} + "KADM5_CONFIG_ADBNAME" {set params [lreplace $params 6 6 $value]} + "KADM5_CONFIG_ADB_LOCKFILE" { + set params [lreplace $params 7 7 $value]} + "KADM5_CONFIG_ADMIN_KEYTAB" { + set params [lreplace $params 8 8 $value]} + "KADM5_CONFIG_ACL_FILE" {set params [lreplace $params 9 9 $value]} + "KADM5_CONFIG_DICT_FILE" { + set params [lreplace $params 10 10 $value]} + "KADM5_CONFIG_MKEY_FROM_KBD" { + set params [lreplace $params 11 11 $value]} + "KADM5_CONFIG_STASH_FILE" { + set params [lreplace $params 12 12 $value]} + "KADM5_CONFIG_MKEY_NAME" { + set params [lreplace $params 13 13 $value]} + "KADM5_CONFIG_ENCTYPE" {set params [lreplace $params 14 14 $value]} + "KADM5_CONFIG_MAX_LIFE" { + set params [lreplace $params 15 15 $value]} + "KADM5_CONFIG_MAX_RLIFE" { + set params [lreplace $params 16 16 $value]} + "KADM5_CONFIG_EXPIRATION" { + set params [lreplace $params 17 17 $value]} + "KADM5_CONFIG_FLAGS" {set params [lreplace $params 18 18 $value]} + "KADM5_CONFIG_ENCTYPES" { + set params [lreplace $params 19 20 [llength $value] $value]} + "*" {error "config_params: unknown mask $mask"} + } + } + return $params +} + + + diff --git a/src/kadmin/testing/util/ChangeLog b/src/kadmin/testing/util/ChangeLog new file mode 100644 index 000000000..36df4616d --- /dev/null +++ b/src/kadmin/testing/util/ChangeLog @@ -0,0 +1,3 @@ +Fri Jul 12 15:04:52 1996 Marc Horowitz + + * tcl_ovsec_kadm.c: renamed to diff --git a/src/kadmin/testing/util/Makefile.ov b/src/kadmin/testing/util/Makefile.ov new file mode 100644 index 000000000..6d0159092 --- /dev/null +++ b/src/kadmin/testing/util/Makefile.ov @@ -0,0 +1,34 @@ +# $Id$ + +TOP = ../.. +include $(TOP)/config.mk/template + +CFLAGS += -I$(TCLINC) + +SRCS = tcl_ovsec_kadm.c tcl_kadm5.c test.c +OBJS = tcl_ovsec_kadm.o tcl_kadm5.o test.o + +PROG = ovsec_kadm_srv_tcl +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \ + $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \ + $(NDBMLIB) $(NETLIB) + +expand Program +expand Depend + +PROG = ovsec_kadm_clnt_tcl +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \ + $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \ + $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand Program + +PROG = bsddb_dump +SRCS = bsddb_dump.c +OBJS = bsddb_dump.o +LIBS = $(LIBDB) + +expand Program +expand Depend diff --git a/src/kadmin/testing/util/bsddb_dump.c b/src/kadmin/testing/util/bsddb_dump.c new file mode 100644 index 000000000..ba69b8461 --- /dev/null +++ b/src/kadmin/testing/util/bsddb_dump.c @@ -0,0 +1,64 @@ +/* + * $Id$ + */ + +#include +#include +#include +#include + +main(int argc, char *argv[]) +{ + char *file; + DB *db; + DBT dbkey, dbdata; + int code, i; + + HASHINFO info; + + info.hash = NULL; + info.bsize = 256; + info.ffactor = 8; + info.nelem = 25000; + info.lorder = 0; + + if (argc != 2) { + fprintf(stderr, "usage: argv[0] dbfile\n"); + exit(2); + } + + file = argv[1]; + + if((db = dbopen(file, O_RDWR, 0666, DB_HASH, &info)) == NULL) { + perror("Opening db file"); + exit(1); + } + + if ((code = (*db->seq)(db, &dbkey, &dbdata, R_FIRST)) == -1) { + perror("starting db iteration"); + exit(1); + } + + while (code == 0) { + for (i=0; iseq)(db, &dbkey, &dbdata, R_NEXT); + } + + if (code == -1) { + perror("during db iteration"); + exit(1); + } + + if ((*db->close)(db) == -1) { + perror("closing db"); + exit(1); + } + + exit(0); +} diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c new file mode 100644 index 000000000..b102cdfc2 --- /dev/null +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -0,0 +1,2312 @@ +#include +#include +#include +#define USE_KADM5_API_VERSION 2 +#include +#include +#include +#include +#include +#include + +struct flagval { + char *name; + krb5_flags val; +}; + +/* XXX This should probably be in the hash table like server_handle */ +static krb5_context context; + +static struct flagval krb5_flags_array[] = { + {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED}, + {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE}, + {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED}, + {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE}, + {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE}, + {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY}, + {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX}, + {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH}, + {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH}, + {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE}, + {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR}, + {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE} +}; + +static struct flagval aux_attributes[] = { + {"KADM5_POLICY", KADM5_POLICY} +}; + +static struct flagval principal_mask_flags[] = { + {"KADM5_PRINCIPAL", KADM5_PRINCIPAL}, + {"KADM5_PRINC_EXPIRE_TIME", KADM5_PRINC_EXPIRE_TIME}, + {"KADM5_PW_EXPIRATION", KADM5_PW_EXPIRATION}, + {"KADM5_LAST_PWD_CHANGE", KADM5_LAST_PWD_CHANGE}, + {"KADM5_ATTRIBUTES", KADM5_ATTRIBUTES}, + {"KADM5_MAX_LIFE", KADM5_MAX_LIFE}, + {"KADM5_MOD_TIME", KADM5_MOD_TIME}, + {"KADM5_MOD_NAME", KADM5_MOD_NAME}, + {"KADM5_KVNO", KADM5_KVNO}, + {"KADM5_MKVNO", KADM5_MKVNO}, + {"KADM5_AUX_ATTRIBUTES", KADM5_AUX_ATTRIBUTES}, + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_POLICY_CLR", KADM5_POLICY_CLR}, + {"KADM5_MAX_RLIFE", KADM5_MAX_RLIFE}, + {"KADM5_LAST_SUCCESS", KADM5_LAST_SUCCESS}, + {"KADM5_LAST_FAILED", KADM5_LAST_FAILED}, + {"KADM5_FAIL_AUTH_COUNT", KADM5_FAIL_AUTH_COUNT}, + {"KADM5_KEY_DATA", KADM5_KEY_DATA}, + {"KADM5_TL_DATA", KADM5_TL_DATA}, + {"KADM5_PRINCIPAL_NORMAL_MASK", KADM5_PRINCIPAL_NORMAL_MASK} +}; + +static struct flagval policy_mask_flags[] = { + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_PW_MAX_LIFE", KADM5_PW_MAX_LIFE}, + {"KADM5_PW_MIN_LIFE", KADM5_PW_MIN_LIFE}, + {"KADM5_PW_MIN_LENGTH", KADM5_PW_MIN_LENGTH}, + {"KADM5_PW_MIN_CLASSES", KADM5_PW_MIN_CLASSES}, + {"KADM5_PW_HISTORY_NUM", KADM5_PW_HISTORY_NUM}, + {"KADM5_REF_COUNT", KADM5_REF_COUNT} +}; + +static struct flagval config_mask_flags[] = { + {"KADM5_CONFIG_REALM", KADM5_CONFIG_REALM}, + {"KADM5_CONFIG_DBNAME", KADM5_CONFIG_DBNAME}, + {"KADM5_CONFIG_MKEY_NAME", KADM5_CONFIG_MKEY_NAME}, + {"KADM5_CONFIG_MAX_LIFE", KADM5_CONFIG_MAX_LIFE}, + {"KADM5_CONFIG_MAX_RLIFE", KADM5_CONFIG_MAX_RLIFE}, + {"KADM5_CONFIG_EXPIRATION", KADM5_CONFIG_EXPIRATION}, + {"KADM5_CONFIG_FLAGS", KADM5_CONFIG_FLAGS}, + {"KADM5_CONFIG_ADMIN_KEYTAB", KADM5_CONFIG_ADMIN_KEYTAB}, + {"KADM5_CONFIG_STASH_FILE", KADM5_CONFIG_STASH_FILE}, + {"KADM5_CONFIG_ENCTYPE", KADM5_CONFIG_ENCTYPE}, + {"KADM5_CONFIG_ADBNAME", KADM5_CONFIG_ADBNAME}, + {"KADM5_CONFIG_ADB_LOCKFILE", KADM5_CONFIG_ADB_LOCKFILE}, + {"KADM5_CONFIG_PROFILE", KADM5_CONFIG_PROFILE}, + {"KADM5_CONFIG_ACL_FILE", KADM5_CONFIG_ACL_FILE}, + {"KADM5_CONFIG_KADMIND_PORT", KADM5_CONFIG_KADMIND_PORT}, + {"KADM5_CONFIG_ENCTYPES", KADM5_CONFIG_ENCTYPES}, + {"KADM5_CONFIG_ADMIN_SERVER", KADM5_CONFIG_ADMIN_SERVER}, + {"KADM5_CONFIG_DICT_FILE", KADM5_CONFIG_DICT_FILE}, + {"KADM5_CONFIG_MKEY_FROM_KBD", KADM5_CONFIG_MKEY_FROM_KBD}, +}; + +static struct flagval priv_flags[] = { + {"KADM5_PRIV_GET", KADM5_PRIV_GET}, + {"KADM5_PRIV_ADD", KADM5_PRIV_ADD}, + {"KADM5_PRIV_MODIFY", KADM5_PRIV_MODIFY}, + {"KADM5_PRIV_DELETE", KADM5_PRIV_DELETE} +}; + + +static char *arg_error = "wrong # args"; + +static Tcl_HashTable *struct_table = 0; + +static int put_server_handle(Tcl_Interp *interp, void *handle, char **name) +{ + int i = 1, newPtr = 0; + static char buf[20]; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + /* + * Handles from ovsec_kadm_init() and kadm5_init() should not + * be mixed during unit tests, but the API would happily + * accept them. Making the hash entry names different in + * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE + * will fail if presented a handle from the other API. + */ + sprintf(buf, "kadm5_handle%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, handle); + + *name = buf; + + return TCL_OK; +} + +static int get_server_handle(Tcl_Interp *interp, char *name, void **handle) +{ + Tcl_HashEntry *entry; + + if(!strcasecmp(name, "null")) + *handle = 0; + else { + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + if (strncmp(name, "ovsec_kadm_handle", 17) == 0) + Tcl_AppendResult(interp, "ovsec_kadm handle " + "specified for kadm5 api: ", name, 0); + else + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + *handle = (void *) Tcl_GetHashValue(entry); + } + return TCL_OK; +} + +static int remove_server_handle(Tcl_Interp *interp, char *name) +{ + Tcl_HashEntry *entry; + + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + + Tcl_SetHashValue(entry, NULL); + return TCL_OK; +} + +#define GET_HANDLE(num_args, ignored) \ + void *server_handle; \ + char *whoami = argv[0]; \ + argv++, argc--; \ + if (argc != num_args + 1) { \ + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \ + return TCL_ERROR; \ + } \ + { \ + int tcl_ret; \ + if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \ + != TCL_OK) { \ + return tcl_ret; \ + } \ + } \ + argv++, argc--; + +static Tcl_HashTable *create_flag_table(struct flagval *flags, int size) +{ + Tcl_HashTable *table; + Tcl_HashEntry *entry; + int i; + + if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_InitHashTable(table, TCL_STRING_KEYS); + + for (i = 0; i < size; i++) { + int newPtr; + + if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_SetHashValue(entry, &flags[i].val); + } + + return table; +} + + +static Tcl_DString *unparse_str(char *in_str) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + if (! in_str) { + Tcl_DStringAppend(str, "null", -1); + } + else { + Tcl_DStringAppend(str, in_str, -1); + } + + return str; +} + + + +static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str) +{ + if (! in_str) { + *out_str = 0; + } + else if (! strcasecmp(in_str, "null")) { + *out_str = 0; + } + else { + *out_str = in_str; + } + return TCL_OK; +} + + +static void set_ok(Tcl_Interp *interp, char *string) +{ + Tcl_SetResult(interp, "OK", TCL_STATIC); + Tcl_AppendElement(interp, "KADM5_OK"); + Tcl_AppendElement(interp, string); +} + + + +static Tcl_DString *unparse_err(kadm5_ret_t code) +{ + char *code_string, *error_string; + Tcl_DString *dstring; + + switch (code) { + case KADM5_FAILURE: code_string = "KADM5_FAILURE"; break; + case KADM5_AUTH_GET: code_string = "KADM5_AUTH_GET"; break; + case KADM5_AUTH_ADD: code_string = "KADM5_AUTH_ADD"; break; + case KADM5_AUTH_MODIFY: + code_string = "KADM5_AUTH_MODIFY"; break; + case KADM5_AUTH_DELETE: + code_string = "KADM5_AUTH_DELETE"; break; + case KADM5_AUTH_INSUFFICIENT: + code_string = "KADM5_AUTH_INSUFFICIENT"; break; + case KADM5_BAD_DB: code_string = "KADM5_BAD_DB"; break; + case KADM5_DUP: code_string = "KADM5_DUP"; break; + case KADM5_RPC_ERROR: code_string = "KADM5_RPC_ERROR"; break; + case KADM5_NO_SRV: code_string = "KADM5_NO_SRV"; break; + case KADM5_BAD_HIST_KEY: + code_string = "KADM5_BAD_HIST_KEY"; break; + case KADM5_NOT_INIT: code_string = "KADM5_NOT_INIT"; break; + case KADM5_INIT: code_string = "KADM5_INIT"; break; + case KADM5_BAD_PASSWORD: + code_string = "KADM5_BAD_PASSWORD"; break; + case KADM5_UNK_PRINC: code_string = "KADM5_UNK_PRINC"; break; + case KADM5_UNK_POLICY: code_string = "KADM5_UNK_POLICY"; break; + case KADM5_BAD_MASK: code_string = "KADM5_BAD_MASK"; break; + case KADM5_BAD_CLASS: code_string = "KADM5_BAD_CLASS"; break; + case KADM5_BAD_LENGTH: code_string = "KADM5_BAD_LENGTH"; break; + case KADM5_BAD_POLICY: code_string = "KADM5_BAD_POLICY"; break; + case KADM5_BAD_HISTORY: code_string = "KADM5_BAD_HISTORY"; break; + case KADM5_BAD_PRINCIPAL: + code_string = "KADM5_BAD_PRINCIPAL"; break; + case KADM5_BAD_AUX_ATTR: + code_string = "KADM5_BAD_AUX_ATTR"; break; + case KADM5_PASS_Q_TOOSHORT: + code_string = "KADM5_PASS_Q_TOOSHORT"; break; + case KADM5_PASS_Q_CLASS: + code_string = "KADM5_PASS_Q_CLASS"; break; + case KADM5_PASS_Q_DICT: + code_string = "KADM5_PASS_Q_DICT"; break; + case KADM5_PASS_REUSE: code_string = "KADM5_PASS_REUSE"; break; + case KADM5_PASS_TOOSOON: + code_string = "KADM5_PASS_TOOSOON"; break; + case KADM5_POLICY_REF: + code_string = "KADM5_POLICY_REF"; break; + case KADM5_PROTECT_PRINCIPAL: + code_string = "KADM5_PROTECT_PRINCIPAL"; break; + case KADM5_BAD_SERVER_HANDLE: + code_string = "KADM5_BAD_SERVER_HANDLE"; break; + case KADM5_BAD_STRUCT_VERSION: + code_string = "KADM5_BAD_STRUCT_VERSION"; break; + case KADM5_OLD_STRUCT_VERSION: + code_string = "KADM5_OLD_STRUCT_VERSION"; break; + case KADM5_NEW_STRUCT_VERSION: + code_string = "KADM5_NEW_STRUCT_VERSION"; break; + case KADM5_BAD_API_VERSION: + code_string = "KADM5_BAD_API_VERSION"; break; + case KADM5_OLD_LIB_API_VERSION: + code_string = "KADM5_OLD_LIB_API_VERSION"; break; + case KADM5_OLD_SERVER_API_VERSION: + code_string = "KADM5_OLD_SERVER_API_VERSION"; break; + case KADM5_NEW_LIB_API_VERSION: + code_string = "KADM5_NEW_LIB_API_VERSION"; break; + case KADM5_NEW_SERVER_API_VERSION: + code_string = "KADM5_NEW_SERVER_API_VERSION"; break; + case KADM5_SECURE_PRINC_MISSING: + code_string = "KADM5_SECURE_PRINC_MISSING"; break; + case KADM5_NO_RENAME_SALT: + code_string = "KADM5_NO_RENAME_SALT"; break; + case KADM5_BAD_CLIENT_PARAMS: + code_string = "KADM5_BAD_CLIENT_PARAMS"; break; + case KADM5_BAD_SERVER_PARAMS: + code_string = "KADM5_BAD_SERVER_PARAMS"; break; + case KADM5_AUTH_LIST: + code_string = "KADM5_AUTH_LIST"; break; + case KADM5_AUTH_CHANGEPW: + code_string = "KADM5_AUTH_CHANGEPW"; break; + case KADM5_GSS_ERROR: code_string = "KADM5_GSS_ERROR"; break; + case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; + case OSA_ADB_NOENT: code_string = "ENOENT"; break; + case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break; + case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break; + case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break; + case OSA_ADB_BAD_DB: code_string = "Invalid database."; break; + case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break; + case OSA_ADB_BADLOCKMODE: code_string = "OSA_ADB_BADLOCKMODE"; break; + case OSA_ADB_CANTLOCK_DB: code_string = "OSA_ADB_CANTLOCK_DB"; break; + case OSA_ADB_NOTLOCKED: code_string = "OSA_ADB_NOTLOCKED"; break; + case OSA_ADB_NOLOCKFILE: code_string = "OSA_ADB_NOLOCKFILE"; break; + case OSA_ADB_NOEXCL_PERM: code_string = "OSA_ADB_NOEXCL_PERM"; break; + case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break; + case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break; + case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break; + case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break; + case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break; + case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break; + case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break; + case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break; + case KRB5_KDB_TRUNCATED_RECORD: + code_string = "KRB5_KDB_TRUNCATED_RECORD"; break; + case KRB5_KDB_RECURSIVELOCK: + code_string = "KRB5_KDB_RECURSIVELOCK"; break; + case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break; + case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break; + case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break; + case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break; + case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break; + case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break; + case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break; + case KRB5_KDB_INVALIDKEYSIZE: + code_string = "KRB5_KDB_INVALIDKEYSIZE"; break; + case KRB5_KDB_CANTREAD_STORED: + code_string = "KRB5_KDB_CANTREAD_STORED"; break; + case KRB5_KDB_BADSTORED_MKEY: + code_string = "KRB5_KDB_BADSTORED_MKEY"; break; + case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break; + case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break; + case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break; + case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break; + case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break; + case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break; + case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break; + case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break; + case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break; + case KRB5_CONFIG_BADFORMAT: code_string = "KRB5_CONFIG_BADFORMAT"; break; + case EINVAL: code_string = "EINVAL"; break; + case ENOENT: code_string = "ENOENT"; break; + default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break; + } + + error_string = (char *) error_message(code); + + if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX Do we really want to exit? Ok if this is */ + /* just a test program, but what about if it gets */ + /* used for other things later? */ + } + + Tcl_DStringInit(dstring); + + if (! (Tcl_DStringAppendElement(dstring, "ERROR") && + Tcl_DStringAppendElement(dstring, code_string) && + Tcl_DStringAppendElement(dstring, error_string))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + return dstring; +} + + + +static void stash_error(Tcl_Interp *interp, krb5_error_code code) +{ + Tcl_DString *dstring = unparse_err(code); + Tcl_DStringResult(interp, dstring); + Tcl_DStringFree(dstring); + free(dstring); +} + +static Tcl_DString *unparse_key_data(krb5_key_data *key_data, int n_key_data) +{ + Tcl_DString *str; + char buf[2048]; + int i, j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + for (i = 0; i < n_key_data; i++) { + krb5_key_data *key = &key_data[i]; + + Tcl_DStringStartSublist(str); + sprintf(buf, "%d", key->key_data_type[0]); + Tcl_DStringAppendElement(str, buf); + sprintf(buf, "%d", key->key_data_ver > 1 ? + key->key_data_type[1] : -1); + Tcl_DStringAppendElement(str, buf); + if (key->key_data_contents[0]) { + sprintf(buf, "0x"); + for (j = 0; j < key->key_data_length[0]; j++) { + sprintf(buf + 2*(j+1), "%02x", + key->key_data_contents[0][j]); + } + } else *buf = '\0'; + Tcl_DStringAppendElement(str, buf); + Tcl_DStringEndSublist(str); + } + + return str; +} + +static Tcl_DString *unparse_tl_data(krb5_tl_data *tl_data, int n_tl_data) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + if (n_tl_data > 0 && tl_data->tl_data_contents) + Tcl_DStringAppendElement(str, "[cannot unparse tl data yet]"); + else + Tcl_DStringAppendElement(str, ""); + + return str; +} + +static Tcl_DString *unparse_flags(struct flagval *array, int size, + krb5_int32 flags) +{ + int i; + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (i = 0; i < size; i++) { + if (flags & array[i].val) { + Tcl_DStringAppendElement(str, array[i].name); + } + } + + return str; +} + + +static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table, + struct flagval *array, int size, char *str, + krb5_flags *flags) +{ + int tcl_ret, tmp, argc, i, retcode = TCL_OK; + char **argv; + Tcl_HashEntry *entry; + + if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) { + *flags = tmp; + return TCL_OK; + } + Tcl_ResetResult(interp); + + if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) { + return TCL_ERROR; + } + + if (! table) { + table = create_flag_table(array, size); + } + + *flags = 0; + + for (i = 0; i < argc; i++) { + if (! (entry = Tcl_FindHashEntry(table, argv[i]))) { + Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0); + retcode = TCL_ERROR; + break; + } + *flags |= *(krb5_flags *) Tcl_GetHashValue(entry); + } + + free(argv); + return(retcode); +} + +static Tcl_DString *unparse_privs(krb5_flags flags) +{ + return unparse_flags(priv_flags, sizeof(priv_flags) / + sizeof(struct flagval), flags); +} + + +static Tcl_DString *unparse_krb5_flags(krb5_flags flags) +{ + return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) / + sizeof(struct flagval), flags); +} + +static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, krb5_flags_array, + sizeof(krb5_flags_array) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static Tcl_DString *unparse_aux_attributes(krb5_int32 flags) +{ + return unparse_flags(aux_attributes, sizeof(aux_attributes) / + sizeof(struct flagval), flags); +} + + +static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, aux_attributes, + sizeof(aux_attributes) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, principal_mask_flags, + sizeof(principal_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, policy_mask_flags, + sizeof(policy_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static Tcl_DString *unparse_principal_ent(kadm5_principal_ent_t princ) +{ + Tcl_DString *str, *tmp_dstring; + char *tmp; + char buf[20]; + krb5_error_code krb5_ret; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp = 0; /* It looks to me from looking at the library source */ + /* code for krb5_parse_name that the pointer passed into */ + /* it should be initialized to 0 if I want it do be */ + /* allocated automatically. */ + if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) { + /* XXX Do we want to return an error? Not sure. */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->princ_expire_time); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_pwd_change); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->pw_expiration); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->max_life); + Tcl_DStringAppendElement(str, buf); + + tmp = 0; + if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) { + /* XXX */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->mod_date); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_krb5_flags(princ->attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->kvno); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->mkvno); + Tcl_DStringAppendElement(str, buf); + + /* XXX This may be dangerous, because the contents of the policy */ + /* field are undefined if the POLICY bit isn't set. However, I */ + /* think it's a bug for the field not to be null in that case */ + /* anyway, so we should assume that it will be null so that we'll */ + /* catch it if it isn't. */ + + tmp_dstring = unparse_str(princ->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_aux_attributes(princ->aux_attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->max_renewable_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_success); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_failed); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->fail_auth_count); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_key_data); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_tl_data); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_key_data(princ->key_data, princ->n_key_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_tl_data(princ->tl_data, princ->n_tl_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + return str; +} + +static int parse_keysalts(Tcl_Interp *interp, char *list, + krb5_key_salt_tuple **keysalts, + int num_keysalts) +{ + char **argv, **argv1 = NULL; + int i, tmp, argc, argc1, retcode; + + *keysalts = NULL; + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + if (argc != num_keysalts) { + sprintf(interp->result, "%d keysalts specified, " + "but num_keysalts is %d", argc, num_keysalts); + retcode = TCL_ERROR; + goto finished; + } + *keysalts = (krb5_key_salt_tuple *) + malloc(sizeof(krb5_key_salt_tuple)*num_keysalts); + for (i = 0; i < num_keysalts; i++) { + if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) != + TCL_OK) { + goto finished; + } + if (argc1 != 2) { + sprintf(interp->result, "wrong # fields in keysalt " + "(%d should be 2)", argc1); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_enctype"); + retcode = TCL_ERROR; + goto finished; + } + (*keysalts)[i].ks_enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_salttype"); + goto finished; + } + (*keysalts)[i].ks_salttype = tmp; + + free(argv1); + } + +finished: + if (argv1) + free(argv1); + if (*keysalts) + free(*keysalts); + free(argv); + return retcode; +} + +static int parse_config_params(Tcl_Interp *interp, char *list, + kadm5_config_params *params) +{ + static Tcl_HashTable *table = 0; + char **argv = NULL; + int tmp, argc, retcode; + + memset(params, 0, sizeof(kadm5_config_params)); + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + + if (argc != 21) { + sprintf(interp->result, + "wrong # args in config params structure (%d should be 21)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if ((retcode = parse_flags(interp, table, config_mask_flags, + sizeof(config_mask_flags) / + sizeof(struct flagval), + argv[0], &tmp)) != TCL_OK) { + goto finished; + } + params->mask = tmp; + + if ((retcode = parse_str(interp, argv[1], ¶ms->realm)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing realm name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[2], ¶ms->profile)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kadmind_port"); + retcode = TCL_ERROR; + goto finished; + } + params->kadmind_port = tmp; + if ((retcode = parse_str(interp, argv[4], ¶ms->admin_server)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[5], ¶ms->dbname)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[6], ¶ms->admin_dbname)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_dbname name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[7], ¶ms->admin_lockfile)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_lockfile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[8], ¶ms->admin_keytab)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_keytab name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[9], ¶ms->acl_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing acl_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[10], ¶ms->dict_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing dict_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[11], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_from_kbd"); + retcode = TCL_ERROR; + goto finished; + } + params->mkey_from_kbd = tmp; + if ((retcode = parse_str(interp, argv[12], ¶ms->stash_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing stash_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[13], ¶ms->mkey_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_name name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[14], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing enctype"); + retcode = TCL_ERROR; + goto finished; + } + params->enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv[15], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + params->max_life = tmp; + if ((retcode = Tcl_GetInt(interp, argv[16], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_rlife"); + retcode = TCL_ERROR; + goto finished; + } + params->max_rlife = tmp; + if ((retcode = Tcl_GetInt(interp, argv[17], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing expiration"); + retcode = TCL_ERROR; + goto finished; + } + params->expiration = tmp; + if ((retcode = parse_krb5_flags(interp, argv[18], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing flags"); + retcode = TCL_ERROR; + goto finished; + } + params->flags = tmp; + if ((retcode = Tcl_GetInt(interp, argv[19], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing num_keysalts"); + retcode = TCL_ERROR; + goto finished; + } + params->num_keysalts = tmp; + if ((retcode = parse_keysalts(interp, argv[20], ¶ms->keysalts, + params->num_keysalts)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keysalts"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + return retcode; +} + +static int parse_principal_ent(Tcl_Interp *interp, char *list, + kadm5_principal_ent_t *out_princ) +{ + kadm5_principal_ent_t princ; + krb5_error_code krb5_ret; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 12) { + sprintf(interp->result, "wrong # args in principal structure (%d should be 12)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (princ = malloc(sizeof *princ))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + retcode = TCL_ERROR; + goto finished; + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing princ_expire_time"); + retcode = TCL_ERROR; + goto finished; + } + princ->princ_expire_time = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_pwd_change"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_pwd_change = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_expiration"); + retcode = TCL_ERROR; + goto finished; + } + princ->pw_expiration = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_life = tmp; + + if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing mod_name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mod_date"); + retcode = TCL_ERROR; + goto finished; + } + princ->mod_date = tmp; + + if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->kvno = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->mkvno = tmp; + + if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + retcode = TCL_ERROR; + goto finished; + } + if(princ->policy != NULL) { + if(!(princ->policy = strdup(princ->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + } + + if ((tcl_ret = parse_aux_attributes(interp, argv[11], + &princ->aux_attributes)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing aux_attributes"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + free(argv); + *out_princ = princ; + return retcode; +} + + +static void free_principal_ent(kadm5_principal_ent_t *princ) +{ + krb5_free_principal(context, (*princ)->principal); + krb5_free_principal(context, (*princ)->mod_name); + free(*princ); + *princ = 0; +} + +static Tcl_DString *unparse_policy_ent(kadm5_policy_ent_t policy) +{ + Tcl_DString *str, *tmp_dstring; + char buf[20]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp_dstring = unparse_str(policy->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", policy->pw_min_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_max_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_length); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_classes); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_history_num); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->policy_refcnt); + Tcl_DStringAppendElement(str, buf); + + return str; +} + + + +static int parse_policy_ent(Tcl_Interp *interp, char *list, + kadm5_policy_ent_t *out_policy) +{ + kadm5_policy_ent_t policy; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 7) { + sprintf(interp->result, "wrong # args in policy structure (%d should be 7)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (policy = malloc(sizeof *policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + retcode = TCL_ERROR; + goto finished; + } + + if(policy->policy != NULL) { + if (! (policy->policy = strdup(policy->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_length"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_length = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_classes"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_classes = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_history_num"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_history_num = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy_refcnt"); + retcode = TCL_ERROR; + goto finished; + } + policy->policy_refcnt = tmp; + +finished: + free(argv); + *out_policy = policy; + return retcode; +} + + +static void free_policy_ent(kadm5_policy_ent_t *policy) +{ + free(*policy); + *policy = 0; +} + +static Tcl_DString *unparse_keytype(krb5_enctype enctype) +{ + Tcl_DString *str; + char buf[50]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + switch (enctype) { + /* XXX is this right? */ + case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break; + case ENCTYPE_DES_CBC_CRC: + Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break; + default: + sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype); + Tcl_DStringAppend(str, buf, -1); + break; + } + + return str; +} + + +static Tcl_DString *unparse_keyblocks(krb5_keyblock *keyblocks, int num_keys) +{ + Tcl_DString *str; + Tcl_DString *keytype; + int i, j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (j = 0; j < num_keys; j++) { + krb5_keyblock *keyblock = &keyblocks[j]; + + Tcl_DStringStartSublist(str); + + keytype = unparse_keytype(keyblock->enctype); + Tcl_DStringAppendElement(str, keytype->string); + Tcl_DStringFree(keytype); + free(keytype); + if (keyblock->length == 0) { + Tcl_DStringAppendElement(str, "0x00"); + } + else { + Tcl_DStringAppendElement(str, "0x"); + for (i = 0; i < keyblock->length; i++) { + char buf[3]; + sprintf(buf, "%02x", (int) keyblock->contents[i]); + Tcl_DStringAppend(str, buf, -1); + } + } + + Tcl_DStringEndSublist(str); + } + + + return str; +} + +enum init_type { INIT_NONE, INIT_PASS, INIT_CREDS }; + +int _tcl_kadm5_init_any(enum init_type init_type, ClientData clientData, + Tcl_Interp *interp, int argc, char *argv[]) +{ + kadm5_ret_t ret; + char *client_name, *pass, *service_name, *realm; + int tcl_ret; + krb5_ui_4 struct_version, api_version; + char *handle_var; + void *server_handle; + char *handle_name, *params_str; + char *whoami = argv[0]; + kadm5_config_params params; + + argv++, argc--; + + krb5_init_context(&context); + + if (argc != 7) { + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); + return TCL_ERROR; + } + + if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[3], ¶ms_str)) != TCL_OK) || + ((tcl_ret = parse_config_params(interp, params_str, ¶ms)) + != TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) != + TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) != + TCL_OK)) { + return tcl_ret; + } + + handle_var = argv[6]; + + if (! (handle_var && *handle_var)) { + Tcl_SetResult(interp, "must specify server handle variable name", + TCL_STATIC); + return TCL_ERROR; + } + + if (init_type == INIT_CREDS) { + krb5_ccache cc; + + if (pass == NULL) { + if (ret = krb5_cc_default(context, &cc)) { + stash_error(interp, ret); + return TCL_ERROR; + } + } else { + if (ret = krb5_cc_resolve(context, pass, &cc)) { + stash_error(interp, ret); + return TCL_ERROR; + } + } + + ret = kadm5_init_with_creds(client_name, cc, service_name, + ¶ms, struct_version, + api_version, &server_handle); + + (void) krb5_cc_close(context, cc); + } else + ret = kadm5_init(client_name, pass, service_name, ¶ms, + struct_version, api_version, &server_handle); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name)) + != TCL_OK) { + return tcl_ret; + } + + if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) { + return TCL_ERROR; + } + + set_ok(interp, "KADM5 API initialized."); + return TCL_OK; +} + +int tcl_kadm5_init(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_PASS, clientData, interp, argc, argv); +} + +int tcl_kadm5_init_with_creds(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_CREDS, clientData, interp, argc, argv); +} + +int tcl_kadm5_destroy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + kadm5_ret_t ret; + int tcl_ret; + + GET_HANDLE(0, 0); + + ret = kadm5_destroy(server_handle); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) { + return tcl_ret; + } + + set_ok(interp, "KADM5 API deinitialized."); + return TCL_OK; +} + +int tcl_kadm5_create_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *princ_string; + kadm5_principal_ent_t princ = 0; + krb5_int32 mask; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + + GET_HANDLE(3, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) != + TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#endif + +#ifdef OVERRIDE + ret = kadm5_create_principal(server_handle, princ, mask, pw, + override_qual); +#else + ret = kadm5_create_principal(server_handle, princ, mask, pw); +#endif + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Principal created."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + + +int tcl_kadm5_delete_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int tcl_ret; + char *name; + + GET_HANDLE(1, 0); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + return TCL_ERROR; + } + } else princ = NULL; + ret = kadm5_delete_principal(server_handle, princ); + + if(princ != NULL) + krb5_free_principal(context, princ); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Principal deleted."); + return TCL_OK; + } +} + + + +int tcl_kadm5_modify_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *princ_string; + kadm5_principal_ent_t princ = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_principal(server_handle, princ, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Principal modified."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + +int tcl_kadm5_rename_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal source, target; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &source)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing source"); + return TCL_ERROR; + } + + if (krb5_ret = krb5_parse_name(context, argv[1], &target)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing target"); + krb5_free_principal(context, source); + return TCL_ERROR; + } + + ret = kadm5_rename_principal(server_handle, source, target); + + if (ret == KADM5_OK) { + set_ok(interp, "Principal renamed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + + krb5_free_principal(context, source); + krb5_free_principal(context, target); + return retcode; +} + + + +int tcl_kadm5_chpass_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + int tcl_ret; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing password"); + retcode = TCL_ERROR; + goto finished; + } + +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal(server_handle, + princ, pw, override_qual); +#else + ret = kadm5_chpass_principal(server_handle, princ, pw); +#endif + + if (ret == KADM5_OK) { + set_ok(interp, "Password changed."); + goto finished; + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_kadm5_chpass_principal_util(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *new_pw; +#ifdef OVERRIDE + int override_qual; +#endif + char *pw_ret, *pw_ret_var; + char msg_ret[1024], *msg_ret_var; + krb5_error_code krb5_ret; + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(4, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing new password"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } +#endif + if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing msg_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal_util(server_handle, princ, new_pw, +#ifdef OVERRIDE + override_qual, +#endif + pw_ret_var ? &pw_ret : 0, + msg_ret_var ? msg_ret : 0); + + if (ret == KADM5_OK) { + if (pw_ret_var && + (! Tcl_SetVar(interp, pw_ret_var, pw_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, "while setting pw_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + if (msg_ret_var && + (! Tcl_SetVar(interp, msg_ret_var, msg_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, + "while setting msg_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Password changed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_kadm5_randkey_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_keyblock *keyblocks; + int num_keys; + char *keyblock_var, *num_var, buf[50]; + Tcl_DString *keyblock_dstring = 0; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int tcl_ret; + int retcode = TCL_OK; + + GET_HANDLE(3, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + if ((tcl_ret = parse_str(interp, argv[2], &num_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_randkey_principal(server_handle, + princ, keyblock_var ? &keyblocks : 0, + num_var ? &num_keys : 0); + + if (ret == KADM5_OK) { + if (keyblock_var) { + keyblock_dstring = unparse_keyblocks(keyblocks, num_keys); + if (! Tcl_SetVar(interp, keyblock_var, + keyblock_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting keyblock variable"); + retcode = TCL_ERROR; + goto finished; + } + } + if (num_var) { + sprintf(buf, "%d", num_keys); + if (! Tcl_SetVar(interp, num_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting num_keys variable"); + } + } + set_ok(interp, "Key randomized."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + if (keyblock_dstring) { + Tcl_DStringFree(keyblock_dstring); + free(keyblock_dstring); + } + return retcode; +} + + + +int tcl_kadm5_get_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + kadm5_principal_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *ent_var; + char *name; + krb5_error_code krb5_ret; + int tcl_ret; + kadm5_ret_t ret = -1; + krb5_int32 mask; + int retcode = TCL_OK; + + GET_HANDLE(3, 1); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + } else princ = NULL; + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + retcode = TCL_ERROR; + goto finished; + } + if ((tcl_ret = parse_principal_mask(interp, argv[2], &mask)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal mask"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_get_principal(server_handle, princ, ent_var ? &ent : 0, + mask); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_principal_ent(&ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if(princ != NULL) + krb5_free_principal(context, princ); + if (ret == KADM5_OK && ent_var && + (ret = kadm5_free_principal_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + +int tcl_kadm5_create_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *policy_string; + kadm5_policy_ent_t policy = 0; + krb5_int32 mask; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + ret = kadm5_create_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Policy created."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + + +int tcl_kadm5_delete_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + char *policy; + int tcl_ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + ret = kadm5_delete_policy(server_handle, policy); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Policy deleted."); + return TCL_OK; + } +} + + + +int tcl_kadm5_modify_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *policy_string; + kadm5_policy_ent_t policy = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Policy modified."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + +int tcl_kadm5_get_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + kadm5_policy_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *policy; + char *ent_var; + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_policy(server_handle, policy, ent_var ? &ent : 0); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_policy_ent(&ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if (ent_var && ret == KADM5_OK && + (ret = kadm5_free_policy_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + + + +int tcl_kadm5_free_principal_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + kadm5_principal_ent_t ent; + int tcl_ret; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_principal_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "principal", sizeof("principal")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "principal handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = kadm5_free_principal_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Principal freed."); + return TCL_OK; +} + + +int tcl_kadm5_free_policy_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + kadm5_policy_ent_t ent; + int tcl_ret; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_policy_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "policy", sizeof("policy")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "policy handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = kadm5_free_policy_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Policy freed."); + return TCL_OK; +} + + +int tcl_kadm5_get_privs(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + char *set_ret; + kadm5_ret_t ret; + char *priv_var; + long privs; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing privs variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_privs(server_handle, priv_var ? &privs : 0); + + if (ret == KADM5_OK) { + if (priv_var) { + Tcl_DString *str = unparse_privs(privs); + set_ret = Tcl_SetVar(interp, priv_var, str->string, + TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(str); + free(str); + if (! set_ret) { + Tcl_AppendElement(interp, "while setting priv variable"); + return TCL_ERROR; + } + } + set_ok(interp, "Privileges retrieved."); + return TCL_OK; + } + else { + stash_error(interp, ret); + return TCL_ERROR; + } +} + + +void Tcl_kadm5_init(Tcl_Interp *interp) +{ + char buf[20]; + + Tcl_SetVar(interp, "KADM5_ADMIN_SERVICE", + KADM5_ADMIN_SERVICE, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "KADM5_CHANGEPW_SERVICE", + KADM5_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_1); + Tcl_SetVar(interp, "KADM5_API_VERSION_1", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_2); + Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION_MASK", buf, + TCL_GLOBAL_ONLY); + + Tcl_CreateCommand(interp, "kadm5_init", tcl_kadm5_init, 0, 0); + Tcl_CreateCommand(interp, "kadm5_init_with_creds", + tcl_kadm5_init_with_creds, 0, 0); + Tcl_CreateCommand(interp, "kadm5_destroy", tcl_kadm5_destroy, 0, + 0); + Tcl_CreateCommand(interp, "kadm5_create_principal", + tcl_kadm5_create_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_principal", + tcl_kadm5_delete_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_principal", + tcl_kadm5_modify_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_rename_principal", + tcl_kadm5_rename_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal", + tcl_kadm5_chpass_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal_util", + tcl_kadm5_chpass_principal_util, 0, 0); + Tcl_CreateCommand(interp, "kadm5_randkey_principal", + tcl_kadm5_randkey_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_principal", + tcl_kadm5_get_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_create_policy", + tcl_kadm5_create_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_policy", + tcl_kadm5_delete_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_policy", + tcl_kadm5_modify_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_policy", + tcl_kadm5_get_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_principal_ent", + tcl_kadm5_free_principal_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_policy_ent", + tcl_kadm5_free_policy_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_privs", + tcl_kadm5_get_privs, 0, 0); +} diff --git a/src/kadmin/testing/util/tcl_krb5_hash.c b/src/kadmin/testing/util/tcl_krb5_hash.c new file mode 100644 index 000000000..95a3451d9 --- /dev/null +++ b/src/kadmin/testing/util/tcl_krb5_hash.c @@ -0,0 +1,163 @@ +/* + * All of the TCL krb5 functions which return (or place into output + * variables) structures or pointers to structures that can't be + * represented as tcl native types, do so by returning a handle for + * the appropriate structure. The handle is a string of the form + * "type$id", where "type" is the type of datum represented by the + * handle and "id" is a unique identifier for it. This handle can + * then be used later by the caller to refer to the object, and + * internally to retrieve the actually datum from the appropriate hash + * table. + * + * The functions in this file do four things: + * + * 1) Given a pointer to a datum and a string representing the type of + * datum to which the pointer refers, create a new handle for the + * datum, store the datum in the hash table using the new handle as + * its key, and return the new handle. + * + * 2) Given a handle, locate and return the appropriate hash table + * datum. + * + * 3) Given a handle, look through a table of types and unparse + * functions to figure out what function to call to get a string + * representation of the datum, call it with the appropriate pointer + * (obtained from the hash table) as an argument, and return the + * resulting string as the unparsed form of the datum. + * + * 4) Given a handle, remove that handle and its associated datum from + * the hash table (but don't free it -- it's assumed to have already + * been freed by the caller). + */ + +#include +#include + +#define SEP_STR "$" + +static char *memory_error = "out of memory"; + +/* + * Right now, we're only using one hash table. However, at some point + * in the future, we might decide to use a separate hash table for + * every type. Therefore, I'm putting this function in as an + * abstraction so it's the only thing we'll have to change if we + * decide to do that. + * + * Also, this function allows us to put in just one place the code for + * checking to make sure that the hash table exists and initializing + * it if it doesn't. + */ + +static TclHashTable *get_hash_table(Tcl_Interp *interp, + char *type) +{ + static Tcl_HashTable *hash_table = 0; + + if (! hash_table) { + if (! (hash_table = malloc(sizeof(*hash_table)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_InitHashTable(hash_table, TCL_STRING_KEYS); + } + return hash_table; +} + +#define MAX_ID 999999999 +#define ID_BUF_SIZE 10 + +static Tcl_HashEntry *get_new_handle(Tcl_Interp *interp, + char *type) +{ + static unsigned long int id_counter = 0; + Tcl_DString *handle; + char int_buf[ID_BUF_SIZE]; + + if (! (handle = malloc(sizeof(*handle)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_DStringInit(handle); + + assert(id_counter <= MAX_ID); + + sprintf(int_buf, "%d", id_counter++); + + Tcl_DStringAppend(handle, type, -1); + Tcl_DStringAppend(handle, SEP_STR, -1); + Tcl_DStringAppend(handle, int_buf, -1); + + return handle; +} + + +Tcl_DString *tcl_krb5_create_object(Tcl_Interp *interp, + char *type, + ClientData datum) +{ + Tcl_HashTable *table; + Tcl_DString *handle; + Tcl_HashEntry *entry; + int entry_created = 0; + + if (! (table = get_hash_table(interp, type))) { + return 0; + } + + if (! (handle = get_new_handle(interp, type))) { + return 0; + } + + if (! (entry = Tcl_CreateHashEntry(table, handle, &entry_created))) { + Tcl_SetResult(interp, "error creating hash entry", TCL_STATIC); + Tcl_DStringFree(handle); + return TCL_ERROR; + } + + assert(entry_created); + + Tcl_SetHashValue(entry, datum); + + return handle; +} + +ClientData tcl_krb5_get_object(Tcl_Interp *interp, + char *handle) +{ + char *myhandle, *id_ptr; + Tcl_HashTable *table; + Tcl_HashEntry *entry; + + if (! (myhandle = strdup(handle))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + + if (! (id_ptr = index(myhandle, *SEP_STR))) { + free(myhandle); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "malformatted handle \"", handle, + "\"", 0); + return 0; + } + + *id_ptr = '\0'; + + if (! (table = get_hash_table(interp, myhandle))) { + free(myhandle); + return 0; + } + + free(myhandle); + + if (! (entry = Tcl_FindHashEntry(table, handle))) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "no object corresponding to handle \"", + handle, "\"", 0); + return 0; + } + + return(Tcl_GetHashValue(entry)); +} + diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm.c b/src/kadmin/testing/util/tcl_ovsec_kadm.c new file mode 100644 index 000000000..0c6aaac9c --- /dev/null +++ b/src/kadmin/testing/util/tcl_ovsec_kadm.c @@ -0,0 +1,2016 @@ +#include +#include +#include +#define USE_KADM5_API_VERSION 1 +#include +#include +#include +#include +#include +#include + +struct flagval { + char *name; + krb5_flags val; +}; + +/* XXX This should probably be in the hash table like server_handle */ +static krb5_context context; + +struct flagval krb5_flags_array[] = { + {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED}, + {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE}, + {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED}, + {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE}, + {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE}, + {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY}, + {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX}, + {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH}, + {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH}, + {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE}, + {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR}, + {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE} +}; + +struct flagval aux_attributes[] = { + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY} +}; + +struct flagval principal_mask_flags[] = { + {"OVSEC_KADM_PRINCIPAL", OVSEC_KADM_PRINCIPAL}, + {"OVSEC_KADM_PRINC_EXPIRE_TIME", OVSEC_KADM_PRINC_EXPIRE_TIME}, + {"OVSEC_KADM_PW_EXPIRATION", OVSEC_KADM_PW_EXPIRATION}, + {"OVSEC_KADM_LAST_PWD_CHANGE", OVSEC_KADM_LAST_PWD_CHANGE}, + {"OVSEC_KADM_ATTRIBUTES", OVSEC_KADM_ATTRIBUTES}, + {"OVSEC_KADM_MAX_LIFE", OVSEC_KADM_MAX_LIFE}, + {"OVSEC_KADM_MOD_TIME", OVSEC_KADM_MOD_TIME}, + {"OVSEC_KADM_MOD_NAME", OVSEC_KADM_MOD_NAME}, + {"OVSEC_KADM_KVNO", OVSEC_KADM_KVNO}, + {"OVSEC_KADM_MKVNO", OVSEC_KADM_MKVNO}, + {"OVSEC_KADM_AUX_ATTRIBUTES", OVSEC_KADM_AUX_ATTRIBUTES}, + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY}, + {"OVSEC_KADM_POLICY_CLR", OVSEC_KADM_POLICY_CLR} +}; + +struct flagval policy_mask_flags[] = { + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY}, + {"OVSEC_KADM_PW_MAX_LIFE", OVSEC_KADM_PW_MAX_LIFE}, + {"OVSEC_KADM_PW_MIN_LIFE", OVSEC_KADM_PW_MIN_LIFE}, + {"OVSEC_KADM_PW_MIN_LENGTH", OVSEC_KADM_PW_MIN_LENGTH}, + {"OVSEC_KADM_PW_MIN_CLASSES", OVSEC_KADM_PW_MIN_CLASSES}, + {"OVSEC_KADM_PW_HISTORY_NUM", OVSEC_KADM_PW_HISTORY_NUM}, + {"OVSEC_KADM_REF_COUNT", OVSEC_KADM_REF_COUNT} +}; + +struct flagval priv_flags[] = { + {"OVSEC_KADM_PRIV_GET", OVSEC_KADM_PRIV_GET}, + {"OVSEC_KADM_PRIV_ADD", OVSEC_KADM_PRIV_ADD}, + {"OVSEC_KADM_PRIV_MODIFY", OVSEC_KADM_PRIV_MODIFY}, + {"OVSEC_KADM_PRIV_DELETE", OVSEC_KADM_PRIV_DELETE} +}; + + +static char *arg_error = "wrong # args"; + +static Tcl_HashTable *struct_table = 0; + +static int put_server_handle(Tcl_Interp *interp, void *handle, char **name) +{ + int i = 1, newPtr = 0; + static char buf[20]; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + /* + * Handles from ovsec_kadm_init() and kadm5_init() should not + * be mixed during unit tests, but the API would happily + * accept them. Making the hash entry names different in + * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE + * will fail if presented a handle from the other API. + */ + sprintf(buf, "ovsec_kadm_handle%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, handle); + + *name = buf; + + return TCL_OK; +} + +static int get_server_handle(Tcl_Interp *interp, char *name, void **handle) +{ + Tcl_HashEntry *entry; + + if(!strcasecmp(name, "null")) + *handle = 0; + else { + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + if (strncmp(name, "kadm5_handle", 12) == 0) + Tcl_AppendResult(interp, "kadm5 handle specified " + "for ovsec_kadm api: ", name, 0); + else + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + *handle = (void *) Tcl_GetHashValue(entry); + } + return TCL_OK; +} + +static int remove_server_handle(Tcl_Interp *interp, char *name) +{ + Tcl_HashEntry *entry; + + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + + Tcl_DeleteHashEntry(entry); + return TCL_OK; +} + +#define GET_HANDLE(num_args, do_dostruct) \ + void *server_handle; \ + int dostruct = 0; \ + char *whoami = argv[0]; \ + argv++, argc--; \ + if ((argc > 0) && (! strcmp(argv[0], "-struct"))) { \ + if (! do_dostruct) { \ + Tcl_AppendResult(interp, "-struct isn't a valid option for ", \ + whoami, 0); \ + return TCL_ERROR; \ + } \ + dostruct++; \ + argv++, argc--; \ + } \ + if (argc != num_args + 1) { \ + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \ + return TCL_ERROR; \ + } \ + { \ + int tcl_ret; \ + if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \ + != TCL_OK) { \ + return tcl_ret; \ + } \ + } \ + argv++, argc--; + +static Tcl_HashTable *create_flag_table(struct flagval *flags, int size) +{ + Tcl_HashTable *table; + Tcl_HashEntry *entry; + int i; + + if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_InitHashTable(table, TCL_STRING_KEYS); + + for (i = 0; i < size; i++) { + int newPtr; + + if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_SetHashValue(entry, &flags[i].val); + } + + return table; +} + + +static Tcl_DString *unparse_str(char *in_str) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + if (! in_str) { + Tcl_DStringAppend(str, "null", -1); + } + else { + Tcl_DStringAppend(str, in_str, -1); + } + + return str; +} + + + +static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str) +{ + if (! in_str) { + *out_str = 0; + } + else if (! strcasecmp(in_str, "null")) { + *out_str = 0; + } + else { + *out_str = in_str; + } + return TCL_OK; +} + + +static void set_ok(Tcl_Interp *interp, char *string) +{ + Tcl_SetResult(interp, "OK", TCL_STATIC); + Tcl_AppendElement(interp, "OVSEC_KADM_OK"); + Tcl_AppendElement(interp, string); +} + + + +static Tcl_DString *unparse_err(ovsec_kadm_ret_t code) +{ + char *code_string, *error_string; + Tcl_DString *dstring; + + switch (code) { + case OVSEC_KADM_FAILURE: code_string = "OVSEC_KADM_FAILURE"; break; + case OVSEC_KADM_AUTH_GET: code_string = "OVSEC_KADM_AUTH_GET"; break; + case OVSEC_KADM_AUTH_ADD: code_string = "OVSEC_KADM_AUTH_ADD"; break; + case OVSEC_KADM_AUTH_MODIFY: + code_string = "OVSEC_KADM_AUTH_MODIFY"; break; + case OVSEC_KADM_AUTH_DELETE: + code_string = "OVSEC_KADM_AUTH_DELETE"; break; + case OVSEC_KADM_AUTH_INSUFFICIENT: + code_string = "OVSEC_KADM_AUTH_INSUFFICIENT"; break; + case OVSEC_KADM_BAD_DB: code_string = "OVSEC_KADM_BAD_DB"; break; + case OVSEC_KADM_DUP: code_string = "OVSEC_KADM_DUP"; break; + case OVSEC_KADM_RPC_ERROR: code_string = "OVSEC_KADM_RPC_ERROR"; break; + case OVSEC_KADM_NO_SRV: code_string = "OVSEC_KADM_NO_SRV"; break; + case OVSEC_KADM_BAD_HIST_KEY: + code_string = "OVSEC_KADM_BAD_HIST_KEY"; break; + case OVSEC_KADM_NOT_INIT: code_string = "OVSEC_KADM_NOT_INIT"; break; + case OVSEC_KADM_INIT: code_string = "OVSEC_KADM_INIT"; break; + case OVSEC_KADM_BAD_PASSWORD: + code_string = "OVSEC_KADM_BAD_PASSWORD"; break; + case OVSEC_KADM_UNK_PRINC: code_string = "OVSEC_KADM_UNK_PRINC"; break; + case OVSEC_KADM_UNK_POLICY: code_string = "OVSEC_KADM_UNK_POLICY"; break; + case OVSEC_KADM_BAD_MASK: code_string = "OVSEC_KADM_BAD_MASK"; break; + case OVSEC_KADM_BAD_CLASS: code_string = "OVSEC_KADM_BAD_CLASS"; break; + case OVSEC_KADM_BAD_LENGTH: code_string = "OVSEC_KADM_BAD_LENGTH"; break; + case OVSEC_KADM_BAD_POLICY: code_string = "OVSEC_KADM_BAD_POLICY"; break; + case OVSEC_KADM_BAD_HISTORY: code_string = "OVSEC_KADM_BAD_HISTORY"; break; + case OVSEC_KADM_BAD_PRINCIPAL: + code_string = "OVSEC_KADM_BAD_PRINCIPAL"; break; + case OVSEC_KADM_BAD_AUX_ATTR: + code_string = "OVSEC_KADM_BAD_AUX_ATTR"; break; + case OVSEC_KADM_PASS_Q_TOOSHORT: + code_string = "OVSEC_KADM_PASS_Q_TOOSHORT"; break; + case OVSEC_KADM_PASS_Q_CLASS: + code_string = "OVSEC_KADM_PASS_Q_CLASS"; break; + case OVSEC_KADM_PASS_Q_DICT: + code_string = "OVSEC_KADM_PASS_Q_DICT"; break; + case OVSEC_KADM_PASS_REUSE: code_string = "OVSEC_KADM_PASS_REUSE"; break; + case OVSEC_KADM_PASS_TOOSOON: + code_string = "OVSEC_KADM_PASS_TOOSOON"; break; + case OVSEC_KADM_POLICY_REF: + code_string = "OVSEC_KADM_POLICY_REF"; break; + case OVSEC_KADM_PROTECT_PRINCIPAL: + code_string = "OVSEC_KADM_PROTECT_PRINCIPAL"; break; + case OVSEC_KADM_BAD_SERVER_HANDLE: + code_string = "OVSEC_KADM_BAD_SERVER_HANDLE"; break; + case OVSEC_KADM_BAD_STRUCT_VERSION: + code_string = "OVSEC_KADM_BAD_STRUCT_VERSION"; break; + case OVSEC_KADM_OLD_STRUCT_VERSION: + code_string = "OVSEC_KADM_OLD_STRUCT_VERSION"; break; + case OVSEC_KADM_NEW_STRUCT_VERSION: + code_string = "OVSEC_KADM_NEW_STRUCT_VERSION"; break; + case OVSEC_KADM_BAD_API_VERSION: + code_string = "OVSEC_KADM_BAD_API_VERSION"; break; + case OVSEC_KADM_OLD_LIB_API_VERSION: + code_string = "OVSEC_KADM_OLD_LIB_API_VERSION"; break; + case OVSEC_KADM_OLD_SERVER_API_VERSION: + code_string = "OVSEC_KADM_OLD_SERVER_API_VERSION"; break; + case OVSEC_KADM_NEW_LIB_API_VERSION: + code_string = "OVSEC_KADM_NEW_LIB_API_VERSION"; break; + case OVSEC_KADM_NEW_SERVER_API_VERSION: + code_string = "OVSEC_KADM_NEW_SERVER_API_VERSION"; break; + case OVSEC_KADM_SECURE_PRINC_MISSING: + code_string = "OVSEC_KADM_SECURE_PRINC_MISSING"; break; + case KADM5_NO_RENAME_SALT: + code_string = "KADM5_NO_RENAME_SALT"; break; + case KADM5_BAD_CLIENT_PARAMS: + code_string = "KADM5_BAD_CLIENT_PARAMS"; break; + case KADM5_BAD_SERVER_PARAMS: + code_string = "KADM5_BAD_SERVER_PARAMS"; break; + case KADM5_AUTH_LIST: + code_string = "KADM5_AUTH_LIST"; break; + case KADM5_AUTH_CHANGEPW: + code_string = "KADM5_AUTH_CHANGEPW"; break; + case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; + case OSA_ADB_NOENT: code_string = "ENOENT"; break; + case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break; + case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break; + case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break; + case OSA_ADB_BAD_DB: code_string = "Invalid database."; break; + case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break; + case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break; + case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break; + case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break; + case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break; + case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break; + case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break; + case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break; + case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break; + case KRB5_KDB_TRUNCATED_RECORD: + code_string = "KRB5_KDB_TRUNCATED_RECORD"; break; + case KRB5_KDB_RECURSIVELOCK: + code_string = "KRB5_KDB_RECURSIVELOCK"; break; + case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break; + case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break; + case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break; + case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break; + case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break; + case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break; + case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break; + case KRB5_KDB_INVALIDKEYSIZE: + code_string = "KRB5_KDB_INVALIDKEYSIZE"; break; + case KRB5_KDB_CANTREAD_STORED: + code_string = "KRB5_KDB_CANTREAD_STORED"; break; + case KRB5_KDB_BADSTORED_MKEY: + code_string = "KRB5_KDB_BADSTORED_MKEY"; break; + case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break; + case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break; + case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break; + case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break; + case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break; + case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break; + case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break; + case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break; + case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break; + case EINVAL: code_string = "EINVAL"; break; + case ENOENT: code_string = "ENOENT"; break; + default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break; + } + + error_string = (char *) error_message(code); + + if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX Do we really want to exit? Ok if this is */ + /* just a test program, but what about if it gets */ + /* used for other things later? */ + } + + Tcl_DStringInit(dstring); + + if (! (Tcl_DStringAppendElement(dstring, "ERROR") && + Tcl_DStringAppendElement(dstring, code_string) && + Tcl_DStringAppendElement(dstring, error_string))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + return dstring; +} + + + +static void stash_error(Tcl_Interp *interp, krb5_error_code code) +{ + Tcl_DString *dstring = unparse_err(code); + Tcl_DStringResult(interp, dstring); + Tcl_DStringFree(dstring); + free(dstring); +} + + + +static Tcl_DString *unparse_flags(struct flagval *array, int size, + krb5_int32 flags) +{ + int i; + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (i = 0; i < size; i++) { + if (flags & array[i].val) { + Tcl_DStringAppendElement(str, array[i].name); + } + } + + return str; +} + + +static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table, + struct flagval *array, int size, char *str, + krb5_flags *flags) +{ + int tcl_ret, tmp, argc, i, retcode = TCL_OK; + char **argv; + Tcl_HashEntry *entry; + + if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) { + *flags = tmp; + return TCL_OK; + } + Tcl_ResetResult(interp); + + if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) { + return TCL_ERROR; + } + + if (! table) { + table = create_flag_table(array, size); + } + + *flags = 0; + + for (i = 0; i < argc; i++) { + if (! (entry = Tcl_FindHashEntry(table, argv[i]))) { + Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0); + retcode = TCL_ERROR; + break; + } + *flags |= *(krb5_flags *) Tcl_GetHashValue(entry); + } + + free(argv); + return(retcode); +} + +static Tcl_DString *unparse_privs(krb5_flags flags) +{ + return unparse_flags(priv_flags, sizeof(priv_flags) / + sizeof(struct flagval), flags); +} + + +static Tcl_DString *unparse_krb5_flags(krb5_flags flags) +{ + return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) / + sizeof(struct flagval), flags); +} + +static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, krb5_flags_array, + sizeof(krb5_flags_array) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static Tcl_DString *unparse_aux_attributes(krb5_int32 flags) +{ + return unparse_flags(aux_attributes, sizeof(aux_attributes) / + sizeof(struct flagval), flags); +} + + +static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, aux_attributes, + sizeof(aux_attributes) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, principal_mask_flags, + sizeof(principal_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, policy_mask_flags, + sizeof(policy_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static Tcl_DString *unparse_principal_ent(ovsec_kadm_principal_ent_t princ) +{ + Tcl_DString *str, *tmp_dstring; + char *tmp; + char buf[20]; + krb5_error_code krb5_ret; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp = 0; /* It looks to me from looking at the library source */ + /* code for krb5_parse_name that the pointer passed into */ + /* it should be initialized to 0 if I want it do be */ + /* allocated automatically. */ + if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) { + /* XXX Do we want to return an error? Not sure. */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->princ_expire_time); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_pwd_change); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->pw_expiration); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->max_life); + Tcl_DStringAppendElement(str, buf); + + tmp = 0; + if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) { + /* XXX */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->mod_date); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_krb5_flags(princ->attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->kvno); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->mkvno); + Tcl_DStringAppendElement(str, buf); + + /* XXX This may be dangerous, because the contents of the policy */ + /* field are undefined if the POLICY bit isn't set. However, I */ + /* think it's a bug for the field not to be null in that case */ + /* anyway, so we should assume that it will be null so that we'll */ + /* catch it if it isn't. */ + + tmp_dstring = unparse_str(princ->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_aux_attributes(princ->aux_attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + return str; +} + + + +static int parse_principal_ent(Tcl_Interp *interp, char *list, + ovsec_kadm_principal_ent_t *out_princ) +{ + ovsec_kadm_principal_ent_t princ; + krb5_error_code krb5_ret; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 12) { + sprintf(interp->result, "wrong # args in principal structure (%d should be 12)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (princ = malloc(sizeof *princ))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + retcode = TCL_ERROR; + goto finished; + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing princ_expire_time"); + retcode = TCL_ERROR; + goto finished; + } + princ->princ_expire_time = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_pwd_change"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_pwd_change = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_expiration"); + retcode = TCL_ERROR; + goto finished; + } + princ->pw_expiration = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_life = tmp; + + if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing mod_name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mod_date"); + retcode = TCL_ERROR; + goto finished; + } + princ->mod_date = tmp; + + if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->kvno = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->mkvno = tmp; + + if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + retcode = TCL_ERROR; + goto finished; + } + if(princ->policy != NULL) { + if(!(princ->policy = strdup(princ->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + } + + if ((tcl_ret = parse_aux_attributes(interp, argv[11], + &princ->aux_attributes)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing aux_attributes"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + free(argv); + *out_princ = princ; + return retcode; +} + + +static void free_principal_ent(ovsec_kadm_principal_ent_t *princ) +{ + krb5_free_principal(context, (*princ)->principal); + krb5_free_principal(context, (*princ)->mod_name); + free(*princ); + *princ = 0; +} + +static Tcl_DString *unparse_policy_ent(ovsec_kadm_policy_ent_t policy) +{ + Tcl_DString *str, *tmp_dstring; + char buf[20]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp_dstring = unparse_str(policy->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", policy->pw_min_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_max_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_length); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_classes); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_history_num); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->policy_refcnt); + Tcl_DStringAppendElement(str, buf); + + return str; +} + + + +static int parse_policy_ent(Tcl_Interp *interp, char *list, + ovsec_kadm_policy_ent_t *out_policy) +{ + ovsec_kadm_policy_ent_t policy; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 7) { + sprintf(interp->result, "wrong # args in policy structure (%d should be 7)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (policy = malloc(sizeof *policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + retcode = TCL_ERROR; + goto finished; + } + + if(policy->policy != NULL) { + if (! (policy->policy = strdup(policy->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_length"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_length = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_classes"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_classes = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_history_num"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_history_num = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy_refcnt"); + retcode = TCL_ERROR; + goto finished; + } + policy->policy_refcnt = tmp; + +finished: + free(argv); + *out_policy = policy; + return retcode; +} + + +static void free_policy_ent(ovsec_kadm_policy_ent_t *policy) +{ + free(*policy); + *policy = 0; +} + +static Tcl_DString *unparse_keytype(krb5_enctype enctype) +{ + Tcl_DString *str; + char buf[50]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + switch (enctype) { + /* XXX is this right? */ + case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break; + case ENCTYPE_DES_CBC_CRC: + Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break; + default: + sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype); + Tcl_DStringAppend(str, buf, -1); + break; + } + + return str; +} + + +static Tcl_DString *unparse_keyblock(krb5_keyblock *keyblock) +{ + Tcl_DString *str; + Tcl_DString *keytype; + int i; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + keytype = unparse_keytype(keyblock->enctype); + Tcl_DStringAppendElement(str, keytype->string); + Tcl_DStringFree(keytype); + free(keytype); + if (keyblock->length == 0) { + Tcl_DStringAppendElement(str, "0x00"); + } + else { + Tcl_DStringAppendElement(str, "0x"); + for (i = 0; i < keyblock->length; i++) { + char buf[3]; + sprintf(buf, "%02x", (int) keyblock->contents[i]); + Tcl_DStringAppend(str, buf, -1); + } + } + + return str; +} + + + +int tcl_ovsec_kadm_init(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_ret_t ret; + char *client_name, *pass, *service_name, *realm; + int tcl_ret; + krb5_ui_4 struct_version, api_version; + char *handle_var; + void *server_handle; + char *handle_name; + char *whoami = argv[0]; + + argv++, argc--; + + krb5_init_context(&context); + + if (argc != 7) { + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); + return TCL_ERROR; + } + + if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[3], &realm)) != TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) != + TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) != + TCL_OK)) { + return tcl_ret; + } + + handle_var = argv[6]; + + if (! (handle_var && *handle_var)) { + Tcl_SetResult(interp, "must specify server handle variable name", + TCL_STATIC); + return TCL_ERROR; + } + + ret = ovsec_kadm_init(client_name, pass, service_name, realm, + struct_version, api_version, &server_handle); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name)) + != TCL_OK) { + return tcl_ret; + } + + if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) { + return TCL_ERROR; + } + + set_ok(interp, "OV Admin system initialized."); + return TCL_OK; +} + + + +int tcl_ovsec_kadm_destroy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_ret_t ret; + int tcl_ret; + + GET_HANDLE(0, 0); + + ret = ovsec_kadm_destroy(server_handle); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) { + return tcl_ret; + } + + set_ok(interp, "OV Admin system deinitialized."); + return TCL_OK; +} + +int tcl_ovsec_kadm_create_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + char *princ_string; + ovsec_kadm_principal_ent_t princ = 0; + krb5_int32 mask; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + + GET_HANDLE(3, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) != + TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#endif + +#ifdef OVERRIDE + ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw, + override_qual); +#else + ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw); +#endif + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Principal created."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + + +int tcl_ovsec_kadm_delete_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int tcl_ret; + char *name; + + GET_HANDLE(1, 0); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + return TCL_ERROR; + } + } else princ = NULL; + ret = ovsec_kadm_delete_principal(server_handle, princ); + + if(princ != NULL) + krb5_free_principal(context, princ); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Principal deleted."); + return TCL_OK; + } +} + + + +int tcl_ovsec_kadm_modify_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *princ_string; + ovsec_kadm_principal_ent_t princ = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_modify_principal(server_handle, princ, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Principal modified."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + +int tcl_ovsec_kadm_rename_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal source, target; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &source)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing source"); + return TCL_ERROR; + } + + if (krb5_ret = krb5_parse_name(context, argv[1], &target)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing target"); + krb5_free_principal(context, source); + return TCL_ERROR; + } + + ret = ovsec_kadm_rename_principal(server_handle, source, target); + + if (ret == OVSEC_KADM_OK) { + set_ok(interp, "Principal renamed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + + krb5_free_principal(context, source); + krb5_free_principal(context, target); + return retcode; +} + + + +int tcl_ovsec_kadm_chpass_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + int tcl_ret; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing password"); + retcode = TCL_ERROR; + goto finished; + } + +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_chpass_principal(server_handle, + princ, pw, override_qual); +#else + ret = ovsec_kadm_chpass_principal(server_handle, princ, pw); +#endif + + if (ret == OVSEC_KADM_OK) { + set_ok(interp, "Password changed."); + goto finished; + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_ovsec_kadm_chpass_principal_util(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *new_pw; +#ifdef OVERRIDE + int override_qual; +#endif + char *pw_ret, *pw_ret_var; + char msg_ret[1024], *msg_ret_var; + krb5_error_code krb5_ret; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(4, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing new password"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } +#endif + if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing msg_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_chpass_principal_util(server_handle, princ, new_pw, +#ifdef OVERRIDE + override_qual, +#endif + pw_ret_var ? &pw_ret : 0, + msg_ret_var ? msg_ret : 0); + + if (ret == OVSEC_KADM_OK) { + if (pw_ret_var && + (! Tcl_SetVar(interp, pw_ret_var, pw_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, "while setting pw_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + if (msg_ret_var && + (! Tcl_SetVar(interp, msg_ret_var, msg_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, + "while setting msg_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Password changed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_ovsec_kadm_randkey_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_keyblock *keyblock; + char *keyblock_var; + Tcl_DString *keyblock_dstring = 0; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int tcl_ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_randkey_principal(server_handle, + princ, keyblock_var ? &keyblock : 0, + override_qual); +#else + ret = ovsec_kadm_randkey_principal(server_handle, + princ, keyblock_var ? &keyblock : 0); +#endif + + if (ret == OVSEC_KADM_OK) { + if (keyblock_var) { + keyblock_dstring = unparse_keyblock(keyblock); + if (! Tcl_SetVar(interp, keyblock_var, + keyblock_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting keyblock variable"); + retcode = TCL_ERROR; + goto finished; + } + } + set_ok(interp, "Key randomized."); + + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + if (keyblock_dstring) { + Tcl_DStringFree(keyblock_dstring); + free(keyblock_dstring); + } + return retcode; +} + + + +int tcl_ovsec_kadm_get_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + ovsec_kadm_principal_ent_t ent; + Tcl_DString *ent_dstring = 0; + char *ent_var; + char *name; + krb5_error_code krb5_ret; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + } else princ = NULL; + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_get_principal(server_handle, princ, ent_var ? &ent : 0); + + if (ret == OVSEC_KADM_OK) { + if (ent_var) { + if (dostruct) { + char buf[20]; + int i = 1, newPtr = 0; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + sprintf(buf, "principal%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, + &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, ent); + if (! Tcl_SetVar(interp, ent_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + Tcl_DeleteHashEntry(entry); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal structure retrieved."); + } + else { + ent_dstring = unparse_principal_ent(ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal retrieved."); + } + } + } + else { + ent = 0; + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if(princ != NULL) + krb5_free_principal(context, princ); + if (ent && ((! dostruct) || (retcode != TCL_OK))) { + if ((ret = ovsec_kadm_free_principal_ent(server_handle, ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + } + return retcode; +} + +int tcl_ovsec_kadm_create_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + char *policy_string; + ovsec_kadm_policy_ent_t policy = 0; + krb5_int32 mask; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + ret = ovsec_kadm_create_policy(server_handle, policy, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Policy created."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + + +int tcl_ovsec_kadm_delete_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + char *policy; + int tcl_ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_delete_policy(server_handle, policy); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Policy deleted."); + return TCL_OK; + } +} + + + +int tcl_ovsec_kadm_modify_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *policy_string; + ovsec_kadm_policy_ent_t policy = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_modify_policy(server_handle, policy, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Policy modified."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + +int tcl_ovsec_kadm_get_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_policy_ent_t ent; + Tcl_DString *ent_dstring = 0; + char *policy; + char *ent_var; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_get_policy(server_handle, policy, ent_var ? &ent : 0); + + if (ret == OVSEC_KADM_OK) { + if (ent_var) { + if (dostruct) { + char buf[20]; + int i = 1, newPtr = 0; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + sprintf(buf, "policy%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, + &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, ent); + if (! Tcl_SetVar(interp, ent_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + Tcl_DeleteHashEntry(entry); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy structure retrieved."); + } + else { + ent_dstring = unparse_policy_ent(ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy retrieved."); + } + } + } + else { + ent = 0; + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if (ent && ((! dostruct) || (retcode != TCL_OK))) { + if ((ret = ovsec_kadm_free_policy_ent(server_handle, ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + } + return retcode; +} + + + +int tcl_ovsec_kadm_free_principal_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + ovsec_kadm_principal_ent_t ent; + int tcl_ret; + ovsec_kadm_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = ovsec_kadm_free_principal_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "principal", sizeof("principal")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "principal handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = ovsec_kadm_free_principal_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Principal freed."); + return TCL_OK; +} + + +int tcl_ovsec_kadm_free_policy_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + ovsec_kadm_policy_ent_t ent; + int tcl_ret; + ovsec_kadm_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = ovsec_kadm_free_policy_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "policy", sizeof("policy")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "policy handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = ovsec_kadm_free_policy_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Policy freed."); + return TCL_OK; +} + + +int tcl_ovsec_kadm_get_privs(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + char *set_ret; + ovsec_kadm_ret_t ret; + char *priv_var; + long privs; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing privs variable name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_get_privs(server_handle, priv_var ? &privs : 0); + + if (ret == OVSEC_KADM_OK) { + if (priv_var) { + Tcl_DString *str = unparse_privs(privs); + set_ret = Tcl_SetVar(interp, priv_var, str->string, + TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(str); + free(str); + if (! set_ret) { + Tcl_AppendElement(interp, "while setting priv variable"); + return TCL_ERROR; + } + } + set_ok(interp, "Privileges retrieved."); + return TCL_OK; + } + else { + stash_error(interp, ret); + return TCL_ERROR; + } +} + + +void Tcl_ovsec_kadm_init(Tcl_Interp *interp) +{ + char buf[20]; + + Tcl_SetVar(interp, "OVSEC_KADM_ADMIN_SERVICE", + OVSEC_KADM_ADMIN_SERVICE, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "OVSEC_KADM_CHANGEPW_SERVICE", + OVSEC_KADM_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION); + Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_1); + Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_1", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_MASK); + Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION_MASK); + Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION_MASK", buf, + TCL_GLOBAL_ONLY); + + Tcl_CreateCommand(interp, "ovsec_kadm_init", tcl_ovsec_kadm_init, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_destroy", tcl_ovsec_kadm_destroy, 0, + 0); + Tcl_CreateCommand(interp, "ovsec_kadm_create_principal", + tcl_ovsec_kadm_create_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_delete_principal", + tcl_ovsec_kadm_delete_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_modify_principal", + tcl_ovsec_kadm_modify_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_rename_principal", + tcl_ovsec_kadm_rename_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal", + tcl_ovsec_kadm_chpass_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal_util", + tcl_ovsec_kadm_chpass_principal_util, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_randkey_principal", + tcl_ovsec_kadm_randkey_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_principal", + tcl_ovsec_kadm_get_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_create_policy", + tcl_ovsec_kadm_create_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_delete_policy", + tcl_ovsec_kadm_delete_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_modify_policy", + tcl_ovsec_kadm_modify_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_policy", + tcl_ovsec_kadm_get_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_free_principal_ent", + tcl_ovsec_kadm_free_principal_ent, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_free_policy_ent", + tcl_ovsec_kadm_free_policy_ent, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_privs", + tcl_ovsec_kadm_get_privs, 0, 0); +} diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm_syntax b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax new file mode 100644 index 000000000..3fc77fbcb --- /dev/null +++ b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax @@ -0,0 +1,57 @@ +Here's a brief summary of the syntax of the tcl versions of the +ovsec_kadm commands: + +string Can be a string or "null" which will turn into a null pointer +principal_ent A 12-field list in the order of the principal_ent + structure: {string number number number number string + number mask number number string mask} + It can also be "null", like a string, to indicate that + a null structure pointer should be used. +mask Either a number, representing the actual value of the + mask, or a sequence of symbols in a list. Example: + {PRINCIPAL ATTRIBUTES} is a valid principal mask. +boolean "1", "0", "true", "false", etc. +varname The name of a Tcl variable, or "null" to not assign. +policy_ent Similar to principal_ent, but with seven fields, + instead of 12. The first is a string, and the rest + are numbers. + +init + client_name:string pass:string service_name:string + realm:string struct_version:int api_version:int + server_handle_ret:varname +destroy + server_handle:string +create_principal + server_handle:string principal:principal_ent + mask:principal_mask password:string +delete_principal + server_handle:string name:string +modify_principal + server_handle:string principal_principal_ent + mask:principal_mask +rename_principal + server_handle:string source:string target:string +chpass_principal + server_handle:string name:string password:string +chpass_principal_util + server_handle:string name:string password:string + pw_ret:varname msg_ret:varname +randkey_principal + server_handle:string name:string keyblock_var:varname +get_principal [-struct] + server_handle:string name:string princ_var:varname +create_policy + server_handle:string policy:policy_ent mask:policy_mask +delete_policy + server_handle:string name:string +modify_policy + server_handle:string policy:policy_ent mask:policy_mask +get_policy [-struct] + server_handle:string name:string policy_var:varname +free_principal_ent + server_handle:string handle:string +free_policy_ent + server_handle:string handle:string +get_privs + server_handle:string privs:priv_var diff --git a/src/kadmin/testing/util/test.c b/src/kadmin/testing/util/test.c new file mode 100644 index 000000000..75a0fc25f --- /dev/null +++ b/src/kadmin/testing/util/test.c @@ -0,0 +1,32 @@ +#include + +#define IS_TCL_7_5 ((TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 705) + +#if IS_TCL_7_5 +int +main(argc, argv) + int argc; /* Number of command-line arguments. */ + char **argv; /* Values of command-line arguments. */ +{ + Tcl_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} +#else +/* + * The following variable is a special hack that allows applications + * to be linked using the procedure "main" from the Tcl library. The + * variable generates a reference to "main", which causes main to + * be brought in from the library (and all of Tcl with it). + */ + +extern int main(); +int *tclDummyMainPtr = (int *) main; +#endif + +int Tcl_AppInit(Tcl_Interp *interp) +{ + Tcl_ovsec_kadm_init(interp); + Tcl_kadm5_init(interp); + + return(TCL_OK); +} diff --git a/src/kadmin/v4server/ChangeLog b/src/kadmin/v4server/ChangeLog new file mode 100644 index 000000000..604014849 --- /dev/null +++ b/src/kadmin/v4server/ChangeLog @@ -0,0 +1,249 @@ +Thu Jul 18 19:46:49 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Tue Jul 9 17:18:56 1996 Marc Horowitz + + * kadm_stream.c: rename HAS_STDLIB_H to HAVE_STDLIB_H to conform + to the autoconf convention + * configure.in: the old configure.in seemed to be written for some + other directory. Now it's right. + * admin_server.c, kadm_ser_wrap.c, kadm_server.c: renamed + to + * Makefile.in: complete rewrite. + +Thu Mar 21 20:33:43 1996 Richard Basch + + * kadm_funcs.c: new principals were being created with two keys, + one of which the key_data_ver=0 and had no valid data. + +Tue Mar 19 19:42:37 1996 Richard Basch + + * kadm_funcs.c: + changed all references of des-cbc-md5 to des-cbc-crc + fixed uninitialized variable + set kvno modulo 256 in database + +Wed Feb 21 23:34:31 1996 Richard Basch + + * kadm_funcs.c: Initialize the length element of the krb5_db_entry + structure in kadm_princ2entry (add_entry was failing). + +Wed Dec 13 03:51:53 1995 Chris Provenzano (proven@mit.edu) + + * kadm_funcs.c : Remove mkvno for krb5_db_entry + +Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c kadm_ser_wrap.c : + s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g + +Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c, kadm_ser_wrap.c : Remove krb5_enctype + references, and replace with krb5_keytype where appropriate. + +Tue Aug 15 14:31:37 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Replace kadm_find_keytype() + with krb5_dbe_find_keytype(). + + +Thu Aug 10 14:48:26 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_funcs.c - Add kadm_find_keytype() to find a particular key/salt + pair. Use this to find keys instead of assuming that the + right one's in the first slot. + Fix transposed arguments to strncpy(). + Handle mod_princ_data stuff. + Supply saltblock to encrypt_key_data(). + * admin_server, kadm_ser_wrap.c - Use kadm_find_keytype() to find keys. + + +Mon Aug 7 13:30:46 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Brute force substitutions + to get this to compile. + + +Mon Jul 17 15:12:30 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_ser_wrap.c - Add NULL stash file argument to krb5_db_fetch_mkey. + + +Fri Jul 7 16:05:11 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Remove all explicit library handling and LDFLAGS. + * configure.in - Add USE_ and KRB5_LIBRARIES. + + +Tue Jun 27 16:05:27 EDT 1995 Paul Park (pjpark@mit.edu) + * acl_files.c - Change check for return value from fputs(3) from NULL + to EOF. That's what's returned on error. + * admin_server.c - Cast 4th argument of setsockopt(2) to be const char * + * kadm_funcs.c - Cast argument to ctime(3) + * kadm_server.c - Cast first argument to strcpy(3) and strcat(3). + +Tue Jun 20 14:44:54 1995 Tom Yu (tlyu@dragons-lair) + + * configure.in: add tests for TIME_WITH_SYS_TIME and sys/time.h + +Thu Jun 15 17:52:29 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Change explicit library names to -l form, and + change target link line to use $(LD) and associated flags. + Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these were + split out. + * configure.in - Add shared library usage check. + +Fri Jun 9 19:07:25 1995 + + * configure.in: Remove standardized set of autoconf macros, which + are now handled by CONFIG_RULES. + +Fri Jun 9 06:49:36 1995 Ezra Peisach + + * kadm_stream.c (vts_long, stv_long): Change u_long to krb5_ui_4 + + * kadm_server.c (kadm_ser_ckpw): Change u_long to krb5_ui_4 + + * kadm_ser_wrap.c (errpkt, kadm_ser_in): Change u_long to krb5_ui_4 + + * kadm_funcs.c (kadm_add_entry): Change u_long to krb5_ui_4 + + * admin_server.c (process_client): Change u_long to krb5_ui_4 + +Sat May 20 22:33:58 1995 Ezra Peisach + + * kadm_stream.c: Based on presence of stdlib.h, include or declare + malloc. + + * configure.in: Check for stdlib.h + +Sun May 7 13:49:54 1995 Ezra Peisach + + * admin_server.c: Avoid warning of redeclaring POSIX_SIGNALS if + already defined. + +Sat Apr 29 00:34:01 1995 Theodore Y. Ts'o + + * admin_server.c (kadm_listen): Use Posix sigaction() instead of + signal() to set signal handlers. This allows us not to + worry about System V signal semantics. Make the code use + POSIX_SIGNALS by default. + +Fri Apr 28 18:08:05 1995 Mark Eichin + + * Makefile.in (KLIB): put KRB4_LIB inside KLIB. + +Thu Apr 27 13:53:41 1995 Mark Eichin + + * Makefile.in (v4kadmind): use KRB4_LIB directly. + +Thu Apr 20 23:21:42 1995 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c: Don't #include , since that's + automatically included by k5-config.h + +Thu Apr 20 15:26:48 1995 Ezra Peisach (epeisach@kangaroo.mit.edu) + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): krb_int32 should be + krb5_int32. + + * acl_files.c: Declare acl_abort as static at top of file. + +Sun Apr 16 19:10:17 1995 Mark Eichin + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): use krb_int32, not + long, for network 4 byte quantities. Should get rid of the + use of memcpy at some point. + +Sat Mar 25 16:59:55 1995 Mark Eichin + + * kadm_funcs.c (kadm_entry2princ): pass kadm_context in to + krb5_524_conv_principal. + +Tue Mar 14 16:45:18 1995 + + * Makefile.in: Don't link in the V4 DES library; use the des425 + library to avoid linking the DES code in twice. + +Thu Mar 2 12:25:13 1995 Theodore Y. Ts'o + + * Makefile.in (ISODELIB): Remove reference to $(ISODELIB). + +Wed Mar 1 16:30:08 1995 Theodore Y. Ts'o + + * kadm_server.c: Remove declataions of malloc(); should be done by + header files. + + * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket + and -lnsl with WITH_NETLIB check. + +Tue Feb 28 02:24:56 1995 John Gilmore (gnu at toad.com) + + * admin_server.c, kadm_server.c, kadm-server.h: Avoid + includes. + +Tue Feb 7 16:42:54 1995 Mark Eichin + + * kadm_funcs.c (kadm_del_entry): fixed call to db_delete_principal. + +Wed Jan 25 18:42:42 1995 Mark Eichin (eichin@tweedledumber.cygnus.com) + + * kadm_server.h (DEL_ACL_FILE): new define, acl file for V4 delete + function. + * kadm_server.c (kadm_ser_add): new function, wrapper for V4 delete. + * kadm_funcs.c (check_access): declare int; add DEL. + (kadm_del_entry): new function, V4 delete from CNS. + (failadd): fix spelling error in log entry. + +Mon Dec 12 13:21:48 1994 Mark Eichin (eichin@cygnus.com) + + * kadm_funcs.c (kadm_entry2princ, kadm_princ2entry, + kadm_chg_srvtab): V4 and V5 max_life are in *different units* so + use the 60*5 conversion factor. + +Fri Nov 18 15:51:11 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change): Add + magic numbers of keyblock structre. + +Fri Nov 18 01:11:58 1994 Mark Eichin + + * configure.in: use CHECK_SIGNALS instead of expansion (from + epeisach). + +Wed Oct 19 18:53:45 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_ser_wrap.c (kadm_ser_init): Use krb5_use_cstype() to + initialize the master_encblock structure. + +Thu Sep 29 22:41:20 1994 Theodore Y. Ts'o (tytso@dcl) + + * Makefile.in: relink executable if libraries change + +Thu Sep 15 10:53:37 1994 Theodore Y. Ts'o (tytso@dcl) + + * admin_server.c (close_syslog, byebye): Move these two functions + before main(), so that they get declared properly. Otherwise + suncc will refuse to compile the file. + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change, + kadm_chg_srvtab): use krb5_timeofday instead of time(0). + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/Makefile.in b/src/kadmin/v4server/Makefile.in new file mode 100644 index 000000000..01500167b --- /dev/null +++ b/src/kadmin/v4server/Makefile.in @@ -0,0 +1,23 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) \ + -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 -DNEED_SOCKETS + +LOCALINCLUDE = -I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +PROG = kadmind4 +OBJS = kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +all:: $(PROG) + +kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et + +$(OBJS): kadm_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/v4server/Makefile.ov b/src/kadmin/v4server/Makefile.ov new file mode 100644 index 000000000..a365e8ea7 --- /dev/null +++ b/src/kadmin/v4server/Makefile.ov @@ -0,0 +1,42 @@ +TOP = .. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +ETABLES = kadm_err.et +expand ErrorTables + +depend:: kadm_err.h + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c kadm_err.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) \ + $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5_ALL) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +CFLAGS := -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 \ + $(WAIT_FLAGS) $(FCNLT_FLAGS) -I. \ + -I$(TOP)/../include/kerberosIV -I$(TOP)/../../src/include/kerberosIV \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/acl_files.c b/src/kadmin/v4server/acl_files.c new file mode 100644 index 000000000..ae3b0c6bf --- /dev/null +++ b/src/kadmin/v4server/acl_files.c @@ -0,0 +1,536 @@ +/* + * kadmin/v4server/acl_files.c + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + */ + + +/*** Routines for manipulating access control list files ***/ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include "krb.h" + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort(); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +void acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if(fputs(acl_cache[idx].acl->tbl[i], new) == EOF + || putc('\n', new) != '\n') { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/acl_files.doc b/src/kadmin/v4server/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +[.][@] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/admin_server.c b/src/kadmin/v4server/admin_server.c new file mode 100644 index 000000000..7a207d7c5 --- /dev/null +++ b/src/kadmin/v4server/admin_server.c @@ -0,0 +1,684 @@ +/* + * kadmin/v4server/admin_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Top-level loop of the kerberos Administration server + */ + +#include +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include +#endif + +/* define it for now */ +#ifndef POSIX_SIGNALS +#define POSIX_SIGNALS +#endif + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#include +#include +#include +#include +#include + +#ifdef OVSEC_KADM +#include +void *ovsec_handle; +kadm5_config_params params; +#endif + +#include "k5-int.h" +#include +#include +#include +#include "com_err.h" +#include "kadm_server.h" + +#ifdef POSIX_SIGTYPE +#define SIGNAL_RETURN return +#else +#define SIGNAL_RETURN return(0) +#endif + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +krb5_context kadm_context; +int debug; + +/* close the system log file */ +void close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +void byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *lrealm; + extern char *optarg; + extern int fascist_cpw; + +#ifdef OVSEC_KADM + memset(¶ms, 0, sizeof(params)); +#endif + + krb5_init_context(&kadm_context); + krb5_init_ets(kadm_context); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "Df:hnd:a:r:FN")) != EOF) + switch(c) { + case 'D': + debug++; + break; + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'd': +#ifdef OVSEC_KADM + params.dbname = optarg; + params.mask |= KADM5_CONFIG_DBNAME; +#else + if (errval = krb5_db_set_name(kadm_context, optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: + printf("Usage: admin_server [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(kadm_context, &lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + +#ifdef OVSEC_KADM + params.realm = krbrlm; + params.mask |= KADM5_CONFIG_REALM; + + if (errval = kadm5_get_config_params(kadm_context, NULL, NULL, + ¶ms, ¶ms)) { + com_err(argv[0], errval, "while retrieving kadm5 params"); + exit(1); + } + if (errval = krb5_db_set_name(kadm_context, params.dbname)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif /* OVSEC_KADM */ + + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + openlog(argv[0], LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, "V4 admin server starting"); + + errval = krb5_db_init(kadm_context); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(kadm_context, TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm +#ifdef OVSEC_KADM + , ¶ms +#endif + ))==KADM_SUCCESS) { + krb5_db_fini(kadm_context); /* Close the Kerberos database-- + will re-open later */ + errval = kadm_listen(); /* listen for calls to server from + clients */ + } + if (errval != KADM_SUCCESS) { + fprintf(stderr,"error: %s\n",error_message(errval)); + krb5_db_fini(kadm_context); /* Close if error */ + } + close_syslog(); /* Close syslog file, print + closing note */ + byebye(); /* Say bye bye on the terminal + in use */ + return 0; +} /* procedure main */ + + +static void clear_secrets() +{ + krb5_finish_key(kadm_context, &server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype +doexit(sig) + int sig; +{ + exit_now = 1; + SIGNAL_RETURN; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); +#ifdef POSIX_SIGNALS + struct sigaction new_act; + + new_act.sa_handler = doexit; + sigemptyset(&new_act.sa_mask); + sigaction(SIGINT, &new_act, 0); + sigaction(SIGTERM, &new_act, 0); + sigaction(SIGHUP, &new_act, 0); + sigaction(SIGQUIT, &new_act, 0); + sigaction(SIGALRM, &new_act, 0); + new_act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &new_act, 0); + new_act.sa_handler = do_child; + sigaction(SIGCHLD, &new_act, 0); +#else + signal(SIGINT, doexit); + signal(SIGTERM, doexit); + signal(SIGHUP, doexit); + signal(SIGQUIT, doexit); + signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + signal(SIGALRM, doexit); + signal(SIGCHLD, do_child); +#endif + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (debug) { + int one = 1; + if (setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, &one, + sizeof(int)) < 0) { + syslog(LOG_ERR, "setsockopt: %m"); + return KADM_NO_BIND; + } + } + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + krb5_key_data *kdatap; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + cleanexit(1); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + + if (retval = krb5_db_set_name(kadm_context, params.dbname)) { + syslog(LOG_ERR, "%s while setting dbname", error_message(retval)); + cleanexit(1); + } +#endif + +#ifndef NOENCRYPTION + /* Must do it here, since this is after the fork() call */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif /* NOENCRYPTION */ + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (retval = krb5_db_init(kadm_context)) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db: %s", error_message(retval)); + cleanexit(1); + } + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(kadm_context, server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_dbe_find_enctype(kadm_context, + &sprinc_entries, + ENCTYPE_DES_CBC_MD5, + -1, + -1, + &kdatap); + if (status) { + syslog(LOG_ERR, "find enctype failed: %s", error_message(status)); + cleanexit(1); + } + + status = krb5_dbekd_decrypt_key_data(kadm_context, + &server_parm.master_encblock, + kdatap, + &cpw_skey, + (krb5_keysalt *) NULL); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype +do_child(sig) + int sig; +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + if (WTERMSIG(status) || WEXITSTATUS(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; +} + +cleanexit(val) +{ + krb5_db_fini(kadm_context); + clear_secrets(); + exit(val); +} + +void +kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case KADM5_AUTH_GET: + case KADM5_AUTH_ADD: + case KADM5_AUTH_MODIFY: + case KADM5_AUTH_DELETE: + case KADM5_AUTH_INSUFFICIENT: + case KADM5_AUTH_LIST: + case KADM5_AUTH_CHANGEPW: + return KADM_UNAUTH; + case KADM5_BAD_DB: + return KADM_UK_RERROR; + case KADM5_DUP: + case KADM5_POLICY_REF: + return KADM_INUSE; + case KADM5_RPC_ERROR: + return KADM_NO_CONN; + case KADM5_NO_SRV: + return KADM_NO_HOST; + case KADM5_UNK_PRINC: + case KADM5_UNK_POLICY: + return KADM_NOENTRY; + case KADM5_PASS_Q_TOOSHORT: + case KADM5_PASS_Q_CLASS: + case KADM5_PASS_Q_DICT: + case KADM5_PASS_REUSE: + case KADM5_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case KADM5_BAD_PASSWORD: + return KADM_NO_CRED; + case KADM5_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case KADM5_NOT_INIT: + case KADM5_BAD_HIST_KEY: + case KADM5_BAD_MASK: + case KADM5_BAD_CLASS: + case KADM5_BAD_LENGTH: + case KADM5_BAD_POLICY: + case KADM5_BAD_PRINCIPAL: + case KADM5_BAD_AUX_ATTR: + case KADM5_BAD_HISTORY: + case KADM5_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/ChangeLog b/src/kadmin/v4server/attic/ChangeLog new file mode 100644 index 000000000..6eefc24c7 --- /dev/null +++ b/src/kadmin/v4server/attic/ChangeLog @@ -0,0 +1,25 @@ +Thu Jul 18 19:47:58 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/attic/Imakefile b/src/kadmin/v4server/attic/Imakefile new file mode 100644 index 000000000..e1449ef32 --- /dev/null +++ b/src/kadmin/v4server/attic/Imakefile @@ -0,0 +1,49 @@ +# $Source$ +# $Author$ +# $Header$ +# +# Copyright 1989 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, +# please see the file . +# +# Imakefile for Kerberos admin server library. + +DEFINES = $(KRB4DEF) +INCLUDES = $(KRB4INCLUDES) -I. +SRCS = \ + kadm_server.c \ + kadm_funcs.c \ + admin_server.c \ + kadm_ser_wrap.c \ + kadm_stream.c \ + kadm_supp.c \ + kadm_err.c \ + acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +ErrorTableObjectRule() + +all:: v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +NormalProgramTarget(v4kadmind,$(OBJS),$(KDBDEPLIB) $(DEPKLIB), \ + $(KDBLIB) $(KRB4LIB) $(KLIB) ,) + +Krb5InstallServerProgram(v4kadmind) + +clean:: + $(RM) kadm_err.c kadm_err.h + +DependTarget() diff --git a/src/kadmin/v4server/attic/Makefile b/src/kadmin/v4server/attic/Makefile new file mode 100644 index 000000000..d0acac021 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile @@ -0,0 +1,39 @@ +TOP = ../.. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) $(LIBKDB5) \ + $(LIBCRYPTO) $(LIBISODE) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +# XXX the -D's should probably be moved somewhere; in krb5.4.2 they +# are in osconf.h +CFLAGS := -DOVSEC_KADM \ + -DKADM_SYSLOG="\"/krb5/admin_server.syslog\"" \ + -DDEFAULT_ACL_DIR="\"/krb5\"" $(WAIT_FLAGS) $(FCNTL_FLAGS) \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/attic/Makefile.in b/src/kadmin/v4server/attic/Makefile.in new file mode 100644 index 000000000..f5206aa66 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile.in @@ -0,0 +1,53 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) +LDFLAGS = -g + +ISODELIB=@ISODELIB@ +COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a +DBMLIB= +KDBLIB=$(TOPLIBD)/libkdb5.a + +KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a + +KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB) + +LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +SRCS = \ + $(srcdir)/kadm_server.c \ + $(srcdir)/kadm_funcs.c \ + $(srcdir)/admin_server.c \ + $(srcdir)/kadm_ser_wrap.c \ + $(srcdir)/kadm_stream.c \ + $(srcdir)/kadm_supp.c \ + $(srcdir)/kadm_err.c \ + $(srcdir)/acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +all:: kadm_err.h v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +kadm_err.h: kadm_err.et + +v4kadmind: $(OBJS) $(KDBDEPLIB) $(DEPKLIB) + $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KLIB) $(KRB4LIB) $(LIBS) $(KRB4)/lib/libdes.a + +install:: + $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind + +clean:: + $(RM) kadm_err.h kadm_err.c + +clean:: + $(RM) v4kadmind + diff --git a/src/kadmin/v4server/attic/acl_files.c b/src/kadmin/v4server/attic/acl_files.c new file mode 100644 index 000000000..81275ae26 --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.c @@ -0,0 +1,541 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + */ + +#ifndef lint +static char rcsid_acl_files_c[] = "$Id$"; +#endif lint + + +/*** Routines for manipulating access control list files ***/ + +#include +#include +#include +#include +#include +#include +#include +#ifdef NEED_SYS_FCNTL_H +#include +#endif +#include "krb.h" +#include + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort PROTOTYPE((char *acl_file, FILE *f)); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if((fputs(acl_cache[idx].acl->tbl[i], new) == EOF) + || (putc('\n', new) != '\n')) { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/attic/acl_files.doc b/src/kadmin/v4server/attic/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +[.][@] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/attic/aclocal.m4 b/src/kadmin/v4server/attic/aclocal.m4 new file mode 100644 index 000000000..70bf66a7d --- /dev/null +++ b/src/kadmin/v4server/attic/aclocal.m4 @@ -0,0 +1,3 @@ +sinclude([./../../aclocal.m4])dnl +undefine([AC_BUILDTOP])dnl +define(AC_BUILDTOP,[./../..])dnl diff --git a/src/kadmin/v4server/attic/admin_server.c b/src/kadmin/v4server/attic/admin_server.c new file mode 100644 index 000000000..04155bca1 --- /dev/null +++ b/src/kadmin/v4server/attic/admin_server.c @@ -0,0 +1,668 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Top-level loop of the kerberos Administration server + */ + +#include + +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include +#endif + +#include +#include +#include +#include + +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#ifdef _AIX +#include +#endif /* _AIX */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef OVSEC_KADM +#include +void *ovsec_handle; +#endif + +#include +#include +#include +#include "kadm_server.h" + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +int des_debug; /* used by the des425 libraries */ +int debug = 0; + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *db_name, *lrealm; + extern char *optarg; + extern int fascist_cpw; + + krb5_init_ets(); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "f:hnd:Da:r:FN")) != EOF) + switch(c) { + case 'd': + if (errval = krb5_db_set_name(optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } + break; + case 'D': + debug++; + break; +#ifndef OVSEC_KADM + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; +#endif + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: +#ifdef OVSEC_KADM + fprintf(stderr, "Usage: ovsec_v4adm_server [-D] [-h] [-n] [-r realm] [-d dbname] [-a acldir]\n"); + +#else + printf("Usage: admin_server [-D] [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); +#endif + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(&lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + open_syslog(argv[0], "V4 admin server (parent) starting"); + + errval = krb5_db_init(); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm)) != KADM_SUCCESS) { + fprintf(stderr, "error initializing: %s\n", error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + /* detach from the terminal */ + if (!debug) { + if ( +#ifdef KRB5B4 + daemon(0, 0) +#else + errval = krb5_detach_process() +#endif + ) { +#ifdef KRB5B4 + errval = errno; +#endif + fprintf(stderr, "error detaching from terminal: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error detaching from terminal: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + open_syslog(argv[0], "V4 admin server (child) starting"); + } + + krb5_db_fini(); + + if (errval = kadm_listen()) { + fprintf(stderr, "error while listening for requests: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error while listening for requests: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + close_syslog(); + byebye(); + exit(0); +} /* procedure main */ + + +/* open the system log file */ +open_syslog(whoami, message) + char *whoami, *message; +{ + static int opened = 0; + + if (opened) { + closelog(); + } + openlog(whoami, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, message); + opened++; +} + +/* close the system log file */ +close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +static clear_secrets() +{ + krb5_finish_key(&server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype doexit() +{ + exit_now = 1; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); + + (void) signal(SIGINT, doexit); + (void) signal(SIGTERM, doexit); + (void) signal(SIGHUP, doexit); + (void) signal(SIGQUIT, doexit); + (void) signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + (void) signal(SIGALRM, doexit); + (void) signal(SIGCHLD, do_child); + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * + sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + Principal service; + des_cblock skey; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + +#endif + +#if !defined(NOENCRYPTION) + /* Must do it here, since this is after the fork() call. */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (krb5_db_init()) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db"); + cleanexit(1); + } + + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(u_int); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + /* This must be 32 bit integer due to the htonl */ + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_kdb_decrypt_key(&server_parm.master_encblock, + &sprinc_entries.key, + &cpw_skey); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); +#ifdef KRB5_FREE_KEYBLOCK_CONTENTS_EXISTS + krb5_free_keyblock_contents(&cpw_skey); +#else + memset((char*)cpw_skey.contents, 0, cpw_skey.length); + free(cpw_skey.contents); +#endif + + krb5_dbm_db_free_principal(&sprinc_entries, nentries); + + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + if (debug) + return; + else + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype do_child() +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + goto done; /* use goto to avoid figuring out whether to + return a value */ + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); +#endif + + done: +} + +cleanexit(val) +{ + krb5_db_fini(); + clear_secrets(); + exit(val); +} + +void kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case OVSEC_KADM_AUTH_GET: + case OVSEC_KADM_AUTH_ADD: + case OVSEC_KADM_AUTH_MODIFY: + case OVSEC_KADM_AUTH_DELETE: + case OVSEC_KADM_AUTH_INSUFFICIENT: + return KADM_UNAUTH; + case OVSEC_KADM_BAD_DB: + return KADM_UK_RERROR; + case OVSEC_KADM_DUP: + case OVSEC_KADM_POLICY_REF: + return KADM_INUSE; + case OVSEC_KADM_RPC_ERROR: + return KADM_NO_CONN; + case OVSEC_KADM_NO_SRV: + return KADM_NO_HOST; + case OVSEC_KADM_UNK_PRINC: + case OVSEC_KADM_UNK_POLICY: + return KADM_NOENTRY; + case OVSEC_KADM_PASS_Q_TOOSHORT: + case OVSEC_KADM_PASS_Q_CLASS: + case OVSEC_KADM_PASS_Q_DICT: + case OVSEC_KADM_PASS_REUSE: + case OVSEC_KADM_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case OVSEC_KADM_BAD_PASSWORD: + return KADM_NO_CRED; + case OVSEC_KADM_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case OVSEC_KADM_NOT_INIT: + case OVSEC_KADM_BAD_HIST_KEY: + case OVSEC_KADM_BAD_MASK: + case OVSEC_KADM_BAD_CLASS: + case OVSEC_KADM_BAD_LENGTH: + case OVSEC_KADM_BAD_POLICY: + case OVSEC_KADM_BAD_PRINCIPAL: + case OVSEC_KADM_BAD_AUX_ATTR: + case OVSEC_KADM_BAD_HISTORY: + case OVSEC_KADM_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/configure.in b/src/kadmin/v4server/attic/configure.in new file mode 100644 index 000000000..f09ba3a28 --- /dev/null +++ b/src/kadmin/v4server/attic/configure.in @@ -0,0 +1,22 @@ +AC_INIT(admin_server.c) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +AC_HAVE_LIBRARY(socket) +AC_HAVE_LIBRARY(nsl) +AC_HAVE_LIBRARY(-lndbm) +AC_HAVE_LIBRARY(-ldbm) +CHECK_WAIT_TYPE +CHECK_FCNTL +AC_FUNC_CHECK(sigprocmask, +AC_COMPILE_CHECK([sigset_t], +[#include ], +[sigset_t x], +AC_DEFINE(POSIX_SIGNALS))) +AC_PROG_AWK +KRB_INCLUDE +ISODE_INCLUDE +WITH_KRB4 +WITH_KRB5ROOT +AC_OUTPUT(Makefile,[EXTRA_RULES]) diff --git a/src/kadmin/v4server/attic/kadm_err.et b/src/kadmin/v4server/attic/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# . +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/attic/kadm_funcs.c b/src/kadmin/v4server/attic/kadm_funcs.c new file mode 100644 index 000000000..7ce9c7b4f --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_funcs.c @@ -0,0 +1,876 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side database manipulation routines + */ + +#ifndef lint +static char rcsid_kadm_funcs_c[] = +"$Id$"; +#endif lint + +#include +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef NEED_SYS_FCNTL_H +#include +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_error_code retval; + time_t lcltim; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(entry.principal, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + lcltim = entry.expiration; + strncpy(princ->exp_date_txt, ctime(&lcltim), + DATE_SZ); + lcltim = princ->mod_date = entry.mod_date; + strncpy(princ->mod_date_txt, ctime(&lcltim), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life; + princ->kdc_key_ver = entry.mkvno; + princ->key_version = entry.kvno; + retval = krb5_524_conv_principal(entry.mod_name, + princ->mod_name, princ->mod_instance, + realm); + if (retval) + return retval; + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(princ.name, princ.instance, + server_parm.krbrlm, &entry->principal); + if (retval) + return retval; + entry->kvno = princ.key_version; + entry->max_life = princ.max_life; + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->mkvno = server_parm.mkvno; /* XXX */ + entry->expiration = princ.exp_date; + retval = krb5_425_conv_principal(princ.mod_name, princ.mod_instance, + server_parm.krbrlm, &entry->mod_name); + if (retval) + return retval; + entry->mod_date = princ.mod_date; + entry->attributes = princ.attributes; + entry->salt_type = KRB5_KDB_SALTTYPE_V4; +} + +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED addding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb4_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + memset((char *)encpw.contents, 0, encpw.length); + free(encpw.contents); + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + newentry.key = encpw; + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + krb5_db_free_principal(&tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_INUSE); + } else { + newentry.kvno = ++data_i.key_version; + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (newentry.mod_name) + krb5_free_principal(newentry.mod_name); + newentry.mod_name = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(&tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(valsin->name, valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &entry, &numfound, &more); + krb5_free_principal(inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(&entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + krb5_error_code retval; + krb5_principal theprinc, rprinc; + krb5_db_entry newentry, odata; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + krb5_425_conv_principal(valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(theprinc, &newentry, &numfound, &more); + if (retval) { + krb5_free_principal(theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(newentry.principal); + newentry.principal = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + newentry.kvno++; + newentry.mkvno = server_parm.mkvno; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + if (newentry.key.contents) { + memset((char *)newentry.key.contents, 0, newentry.key.length); + free(newentry.key.contents); + } + newentry.key = encpw; + } + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, &odata, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(&odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_encrypted_keyblock encpw; + krb5_db_entry odata; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.keytype = KEYTYPE_DES; + localpw.length = 8; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &localpw, &encpw); + memset((char *)localpw.contents, 0, 8); + free(localpw.contents); + if (retval) { + krb5_free_principal(rprinc); + failchange(retval); + } + numfound = 1; + retval = krb5_db_get_principal(rprinc, &odata, &numfound, &more); + krb5_free_principal(rprinc); + if (retval) { + failchange(retval); + } else if (numfound == 1) { + odata.key = encpw; + odata.kvno++; + odata.mkvno = server_parm.mkvno; + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + FILE *f; + char filename[MAXPATHLEN]; + char buf[ANAME_SZ], *cp; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + Principal principal; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(values->name, values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &odata, &numfound, &more); + if (retval) { + krb5_free_principal(inprinc); + failsrvtab(retval); + } else if (numfound) { + odata.kvno++; + } else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.principal = inprinc; + odata.kvno = 1; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.mkvno = server_parm.mkvno; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + } + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.keytype = KEYTYPE_DES; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.kvno; + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (odata.key.contents) { + memset((char *)odata.key.contents, 0, odata.key.length); + free(odata.key.contents); + } + odata.key = encpw; + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/attic/kadm_ser_wrap.c b/src/kadmin/v4server/attic/kadm_ser_wrap.c new file mode 100644 index 000000000..bca8b8e05 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_ser_wrap.c @@ -0,0 +1,288 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side support functions + */ + +#ifndef lint +static char rcsid_module_c[] = +"$Header$"; +#endif lint + +#include +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kadm_server.h" + +#ifdef OVSEC_KADM +#include +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(&server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + + /* setting up the addrs */ + server_parm.admin_fd = -1; + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + server_parm.master_keyblock.keytype = KEYTYPE_DES; +#ifdef PROVIDE_DES_CBC_CRC +#ifdef KRB5B4 + server_parm.master_encblock.crypto_entry = krb5_des_cst_entry.system; +#else + server_parm.master_encblock.crypto_entry = &mit_des_cryptosystem_entry; +#endif /* KRB5B4 */ +#else + error(You gotta figure out what cryptosystem to use in the KDC); +#endif + retval = krb5_db_setup_mkey_name(mkey_name, realm, (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(&server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + + retval = krb5_db_get_principal(server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = master_entry.kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(&master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb4_uint32 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb4_uint32); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb4_uint32) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb4_uint32)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb4_uint32 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb4_uint32 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb4_uint32); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum((des_cblock *)in_st, (des_cblock *)0, (krb4_int32) r_len, 0, + (des_cblock *)ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb4_uint32))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb4_uint32)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb4_uint32)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb4_uint32)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb4_uint32), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (krb4_uint32) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + free(tmpdat); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + free(tmpdat); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/attic/kadm_server.c b/src/kadmin/v4server/attic/kadm_server.c new file mode 100644 index 000000000..143af5d4e --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.c @@ -0,0 +1,546 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side subroutines + */ + +#ifndef lint +static char rcsid_kadm_server_c[] = +"$Header$"; +#endif lint + +#include + +#include +#include + +#include +#ifdef USE_SYS_TIME_H +#include +#else +#include +#endif +#include + +#ifdef OVSEC_KADM +#include +#include +#include +#include +extern void *ovsec_handle; +#endif + +#include +#include + +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + int a1,a2,a3,a4,a5,a6,a7,a8,a9,a0; +{ + FILE *logfile, *fopen(); + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((((krb5_int32 *)newkey) + 1), &keyhigh, 4); + memcpy(newkey, &keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(&user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(&now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + +#else /* !OVSEC_KADM */ + + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_err); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_warn); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +/**********************************************************************/ +#ifndef OVSEC_KADM + +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, check_pw_msg); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/attic/kadm_server.h b/src/kadmin/v4server/attic/kadm_server.h new file mode 100644 index 000000000..4e6fd8c46 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.h @@ -0,0 +1,64 @@ +/* + * $Source$ + * $Author$ + * $Header$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include +#include +#include + +#include +#include +#include +#include +#ifdef PROVIDE_DES_CBC_CRC +#include +#endif + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/attic/kadm_stream.c b/src/kadmin/v4server/attic/kadm_stream.c new file mode 100644 index 000000000..83aaa295c --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_stream.c @@ -0,0 +1,276 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Stream conversion functions for Kerberos administration server + */ + +#ifndef lint +static char rcsid_kadm_stream_c[] = +"$Header$"; +#endif lint + +#include +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, krb4_int32, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, krb4_int32, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" +#include + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((void *) *st, (void *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb4_uint32 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb4_uint32))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb4_uint32)); + return sizeof(krb4_uint32); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + krb4_int32 lcllong; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &lcllong, stsize, + maxlen)) < 0) + return(-1); + dt_out->exp_date = lcllong; + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb4_uint32 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + if (loc + sizeof(krb4_uint32) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(krb4_uint32)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb4_uint32); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/attic/kadm_supp.c b/src/kadmin/v4server/attic/kadm_supp.c new file mode 100644 index 000000000..cf4ba40f4 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_supp.c @@ -0,0 +1,114 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Support functions for Kerberos administration server & clients + */ + +#ifndef lint +static char rcsid_kadm_supp_c[] = +"$Header$"; +#endif lint + +#include +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime(&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/configure.in b/src/kadmin/v4server/configure.in new file mode 100644 index 000000000..9d45c1e16 --- /dev/null +++ b/src/kadmin/v4server/configure.in @@ -0,0 +1,16 @@ +AC_INIT(admin_server.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_CHECK_HEADERS(sys/time.h unistd.h stdlib.h) +CHECK_SIGNALS +CHECK_WAIT_TYPE +AC_PROG_AWK +USE_KADMCLNT_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +USE_KRB4_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/v4server/kadm_err.et b/src/kadmin/v4server/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# . +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/kadm_funcs.c b/src/kadmin/v4server/kadm_funcs.c new file mode 100644 index 000000000..5025e3acb --- /dev/null +++ b/src/kadmin/v4server/kadm_funcs.c @@ -0,0 +1,1066 @@ +/* + * kadmin/v4server/kadm_funcs.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side database manipulation routines + */ + + +#include +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include +#include +#include +/* #include Gotten by kadmin_server.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#ifdef TIME_WITH_SYS_TIME +#include +#endif +#else +#include +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_tl_mod_princ *mprinc; + krb5_key_data *pkey; + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(kadm_context, entry.princ, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + strncpy(princ->exp_date_txt, ctime((const time_t *) &entry.expiration), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life / (60 * 5); + princ->kdc_key_ver = 1; /* entry.mkvno; */ + princ->key_version = entry.key_data[0].key_data_kvno; + + retval = krb5_dbe_decode_mod_princ_data(kadm_context, &entry, &mprinc); + if (retval) + return retval; + princ->mod_date = mprinc->mod_date; + strncpy(princ->mod_date_txt, + ctime((const time_t *) &mprinc->mod_date), + DATE_SZ); + krb5_free_principal(kadm_context, mprinc->mod_princ); + krb5_xfree(mprinc); + + /* Find the V4 key */ + retval = krb5_dbe_find_enctype(kadm_context, + &entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + return retval; + princ->key_version = pkey->key_data_kvno; + + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + krb5_tl_mod_princ mprinc; + krb5_key_data *kdatap; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(kadm_context, princ.name, princ.instance, + server_parm.krbrlm, &entry->princ); + if (retval) + return retval; + + entry->len = KRB5_KDB_V1_BASE_LENGTH; + entry->max_life = princ.max_life * (60 * 5); + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->expiration = princ.exp_date; + entry->attributes = princ.attributes; + + retval = krb5_425_conv_principal(kadm_context, princ.mod_name, + princ.mod_instance, + server_parm.krbrlm, &mprinc.mod_princ); + if (retval) + return(retval); + mprinc.mod_date = princ.mod_date; + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, &mprinc, entry); + if (retval) + return(retval); + + if (mprinc.mod_princ) + krb5_free_principal(kadm_context, mprinc.mod_princ); + + if (retval = krb5_dbe_find_enctype(kadm_context, + entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &kdatap)) { + if (!(retval = krb5_dbe_create_key_data(kadm_context, entry))) + kdatap = &entry->key_data[entry->n_key_data-1]; + } + if (kdatap) { + kdatap->key_data_ver = 2; + kdatap->key_data_type[0] = (krb5_int16) ENCTYPE_DES_CBC_CRC; + kdatap->key_data_type[1] = (krb5_int16) KRB5_KDB_SALTTYPE_V4; + kdatap->key_data_kvno = (krb5_int16) princ.key_version; + } + return(retval); +} + +int +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case DELACL: + (void) sprintf(filename, "%s%s", acldir, DEL_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED adding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + newpw.magic = KV5M_KEYBLOCK; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + + retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + failadd(retval); + + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb5_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + /* encrypt new key in master key */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) ++data_i.key_version, + pkey); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_INUSE); + } else { + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + mprinc.mod_princ = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define faildel(code) { (void) syslog(LOG_ERR, "FAILED deleting '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_del_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + int numfound; /* check how many we get written */ + krb5_boolean more; /* pointer to more grabbed records */ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_db_entry entry, odata; + krb5_error_code retval; + krb5_principal inprinc; + + if (!check_access(rname, rinstance, rrealm, DELACL)) { + (void) syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to delete an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + faildel(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to delete an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + faildel(retval); + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } else if (!numfound || more) { + faildel(KADM_NOENTRY); + } + + retval = krb5_db_delete_principal(kadm_context, inprinc, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_SERROR); + } else { + if (retval) { + faildel(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_RERROR); + } + kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' deleted.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } +} +#undef faildel + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + krb5_free_principal(kadm_context, inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_error_code retval; + krb5_principal theprinc; + krb5_db_entry newentry, odata; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, + valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(kadm_context, theprinc, &newentry, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(kadm_context, newentry.princ); + newentry.princ = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.magic = KV5M_KEYBLOCK; + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + if (retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(retval); + } + if (pkey->key_data_contents[0]) { + krb5_xfree(pkey->key_data_contents[0]); + pkey->key_data_contents[0] = (krb5_octet *) NULL; + } + /* encrypt new key in master key */ + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) pkey->key_data_kvno+1, + pkey); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + } + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, &odata, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_db_entry odata; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.magic = KV5M_KEYBLOCK; + localpw.enctype = ENCTYPE_DES_CBC_CRC; + localpw.length = 8; + numfound = 1; + retval = krb5_db_get_principal(kadm_context, rprinc, &odata, + &numfound, &more); + krb5_free_principal(kadm_context, rprinc); + if (retval) { + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + failchange(retval); + } else if (numfound == 1) { + if (retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + failchange(retval); + } + pkey->key_data_kvno++; + pkey->key_data_kvno %= 256; + numfound = 1; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &localpw, + &sblock, + (int) pkey->key_data_kvno, + pkey); + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + if (retval) { + failchange(retval); + } + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_key_data *pkey; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(kadm_context, values->name, + values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &odata, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } else if (numfound) { + retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } + } + else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.princ = inprinc; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + if (!krb5_dbe_create_key_data(kadm_context, &odata)) { + pkey = &odata.key_data[0]; + memset(pkey, 0, sizeof(*pkey)); + pkey->key_data_ver = 2; + pkey->key_data_type[0] = ENCTYPE_DES_CBC_CRC; + pkey->key_data_type[1] = KRB5_KDB_SALTTYPE_V4; + } + } + pkey->key_data_kvno++; + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.enctype = ENCTYPE_DES_CBC_CRC; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.max_life / (60 * 5); + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + (krb5_keysalt *) NULL, + (int) pkey->key_data_kvno, + pkey); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (retval) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/kadm_ser_wrap.c b/src/kadmin/v4server/kadm_ser_wrap.c new file mode 100644 index 000000000..5e7f48508 --- /dev/null +++ b/src/kadmin/v4server/kadm_ser_wrap.c @@ -0,0 +1,310 @@ +/* + * kadmin/v4server/kadm_ser_wrap.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side support functions + */ + + +#include +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include "kadm_server.h" +#include +#include +#include +#include + +#ifdef OVSEC_KADM +#include +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +#ifdef OVSEC_KADM +kadm_ser_init(inter, realm, params) + int inter; /* interactive or from file */ + char realm[]; + kadm5_config_params *params; +#else +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +#endif +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + krb5_key_data *kdatap; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(kadm_context, + &server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + server_parm.admin_fd = -1; + /* setting up the addrs */ + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + +#ifdef OVSEC_KADM + server_parm.master_keyblock.enctype = params->enctype; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); +#else + if (inter == 1) { + server_parm.master_keyblock.enctype = ENCTYPE_DES_CBC_MD5; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); + } else + server_parm.master_keyblock.enctype = ENCTYPE_UNKNOWN; +#endif + + retval = krb5_db_setup_mkey_name(kadm_context, mkey_name, realm, + (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(kadm_context, server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, +#ifdef OVSEC_KADM + params->stash_file, +#else + (char *) NULL, +#endif + NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(kadm_context, server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(kadm_context, &server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_db_get_principal(kadm_context, server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + + retval = krb5_dbe_find_enctype(kadm_context, + &master_entry, + -1, -1, -1, + &kdatap); + if (retval) + return KRB5_PROG_KEYTYPE_NOSUPP; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = kdatap->key_data_kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(kadm_context, &master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb5_ui_4 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb5_ui_4) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb5_ui_4 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb5_ui_4 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb5_ui_4); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum(in_st, (krb5_ui_4 *)0, (long) r_len, 0, ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case DEL_ENT: + retval = kadm_ser_del(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb5_ui_4))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb5_ui_4)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb5_ui_4)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb5_ui_4)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb5_ui_4), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (u_long) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/kadm_server.c b/src/kadmin/v4server/kadm_server.c new file mode 100644 index 000000000..81e43f128 --- /dev/null +++ b/src/kadmin/v4server/kadm_server.c @@ -0,0 +1,571 @@ +/* + * kadmin/v4server/kadm_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Kerberos administration server-side subroutines + */ + + +#include + +#include "k5-int.h" + +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#ifdef TIME_WITH_SYS_TIME +#include +#endif +#else +#include +#endif + +#ifdef OVSEC_KADM +#include +#include +#include +#include +extern void *ovsec_handle; +#endif + +#include +#include + +extern krb5_context kadm_context; +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + char *a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9,*a0; +{ + FILE *logfile; + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_int32 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(kadm_context, &user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(kadm_context, &now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(kadm_context, user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } +#else /* OVSEC_KADM */ + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_err); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_warn); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +#ifndef OVSEC_KADM +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +int +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_del - the server side of the del_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Deletes an entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_del(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_del_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, check_pw_msg); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/kadm_server.h b/src/kadmin/v4server/kadm_server.h new file mode 100644 index 000000000..d852bcaab --- /dev/null +++ b/src/kadmin/v4server/kadm_server.h @@ -0,0 +1,58 @@ +/* + * kadmin/v4server/kadm_server.h + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include +#include +#include +#include "k5-int.h" + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define DEL_ACL_FILE "/v4acl.del" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +krb5_context kadm_context; + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/kadm_stream.c b/src/kadmin/v4server/kadm_stream.c new file mode 100644 index 000000000..86da6c64f --- /dev/null +++ b/src/kadmin/v4server/kadm_stream.c @@ -0,0 +1,277 @@ +/* + * kadmin/v4server/kadm_stream.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Stream conversion functions for Kerberos administration server + */ + + +#include +#include +#include "k5-int.h" + +#ifdef HAVE_STDLIB_H +#include +#else +extern char *malloc(), *calloc(), *realloc(); +#endif + +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, long, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, long, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((char *) *st, (char *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb5_ui_4 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4)); + return sizeof(krb5_ui_4); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &dt_out->exp_date, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st+ loc, sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb5_ui_4 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + if (loc + sizeof(krb5_ui_4) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb5_ui_4); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/kadm_supp.c b/src/kadmin/v4server/kadm_supp.c new file mode 100644 index 000000000..9d2f8deb2 --- /dev/null +++ b/src/kadmin/v4server/kadm_supp.c @@ -0,0 +1,113 @@ +/* + * kadmin/v4server/kadm_supp.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + * Support functions for Kerberos administration server & clients + */ + + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +void prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime((long *)&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/unit-test/ChangeLog b/src/kadmin/v4server/unit-test/ChangeLog new file mode 100644 index 000000000..93120b8a4 --- /dev/null +++ b/src/kadmin/v4server/unit-test/ChangeLog @@ -0,0 +1,13 @@ +Mon Jul 15 17:15:51 1996 Marc Horowitz + + * helpers.exp (exp_prog): the check for non-newline-terminated + stdout was causing failures where there weren't any. Barry + doesn't remember why this was here to begin with. + * Makefile.ov (unit-test-body), helpers.exp: some versions of + runtest do not like digits in command-line variable names. + * Makefile.ov (unit-test-body), helpers.exp: ovsec_v4adm_server + renamed to kadmind4 + * getpid.sh: grep out any programs with expect or kadmind4 in + their names. + + diff --git a/src/kadmin/v4server/unit-test/Makefile.ov b/src/kadmin/v4server/unit-test/Makefile.ov new file mode 100644 index 000000000..3af65607e --- /dev/null +++ b/src/kadmin/v4server/unit-test/Makefile.ov @@ -0,0 +1,19 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS_LOCAL) -v4files -kdcport 750 -keysalt des-cbc-crc:v4 + $(LOCAL_MAKE_KEYTAB) -princ changepw/kerberos /krb5/ovsec_adm.srvtab + +unit-test-body:: + $(RUNTEST) VFOURSERVER=../kadmind4 --tool v4server \ + KDBFIVE_EDIT=../../../admin/edit/kdb5_edit + +unit-test-cleanup:: + $(STOP_SERVERS_LOCAL) -v4files diff --git a/src/kadmin/v4server/unit-test/config/ChangeLog b/src/kadmin/v4server/unit-test/config/ChangeLog new file mode 100644 index 000000000..aa01abc17 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/ChangeLog @@ -0,0 +1,7 @@ +Mon Jul 15 17:18:56 1996 Marc Horowitz + + * unix.exp: some versions of runtest do not like digits in + command-line variable names. ovsec_edit_keytab renamed to + kadm5_keytab + + diff --git a/src/kadmin/v4server/unit-test/config/unix.exp b/src/kadmin/v4server/unit-test/config/unix.exp new file mode 100644 index 000000000..874092311 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/unix.exp @@ -0,0 +1,42 @@ +global env + +set kill /bin/kill + +if {[file exists /bin/sleep]} { + set sleep /bin/sleep +} else { + set sleep /usr/bin/sleep +} + +set kpasswd_v4 /usr/athena/bin/kpasswd +set ovpasswd $env(TOP)/kpasswd/kpasswd +set kadmin_local $env(TOP)/cli/kadmin.local +set kdb5_edit $KDBFIVE_EDIT +set remove_changepw_perms ./remove_changepw_perms.sh +set getpid ./getpid.sh +set ovsec_adm_server $env(TOP)/server/kadmind +set ovsec_edit_keytab $env(TOP)/keytab/kadm5_keytab +set hostname [exec hostname] + +# change-password.exp sends ^C to kpasswd to kill it; on HP-UX the +# default intr character is DEL, and setting it on all platforms +# won't hurt +set stty_init "intr \\^c" + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} diff --git a/src/kadmin/v4server/unit-test/getpid.sh b/src/kadmin/v4server/unit-test/getpid.sh new file mode 100644 index 000000000..5c1b1a690 --- /dev/null +++ b/src/kadmin/v4server/unit-test/getpid.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +$PS_ALL | awk "/$1/"' && !/awk/ && !/getpid/ && !/expect/ && !/kadmind4/ { print $2 }' diff --git a/src/kadmin/v4server/unit-test/helpers.exp b/src/kadmin/v4server/unit-test/helpers.exp new file mode 100644 index 000000000..7f23b65c8 --- /dev/null +++ b/src/kadmin/v4server/unit-test/helpers.exp @@ -0,0 +1,232 @@ +proc server_pids { } { + global env + + return [eval [concat exec $env(PS_ALL) | \ + awk {{/kadmind4/ && !/awk/ && !/expect/ {printf("%d ", $2)}}}]] +} + +proc server_exit { name status } { + global wait_error_index wait_errno_index wait_status_index + global server_id + global kill + + verbose "$name: stopping V4 kpasswd server." 1 + + # We can't know whether the process exists or not, so we have + # to ignore errors. XXX will close ever time out? + catch {close $server_id} + set pids [server_pids] + if {$pids != {}} { + verbose "server_exit killing process(es) $pids" + catch {exec $kill $pids} + } else { + verbose "server_exit: couldn't find running server(s) to kill" + } + + # wait hangs on AIX if the process was killed; since status == -1 + # in that case, solve the problem by not waiting; the zombies will + # be cleaned up when the test finishes + if {$status == -1} { + return 1 + } + + set ret [wait -i $server_id] + verbose "% Exit $ret" 2 + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + return 0 + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + return 0 + } + } + + return 1 +} + +proc myfail { msg } { + global mytest_status + fail "$msg" + set mytest_status 1 +} + +proc server_start { name cmdline should_listen args } { + global spawn_id server_id + global VFOURSERVER + global mytest_status + global sleep hostname + + set max_tries 60 + + verbose "$name: starting V4 kpasswd server." 1 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "Couldn't connect to V4 kpasswd server; retrying ($num_tries so far)." + } + + spawn $VFOURSERVER $cmdline + set server_id $spawn_id + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + -i $server_id + $test + timeout { myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { + return 0 + } + } + + set pids [server_pids] + + if {$should_listen} { + exec $sleep 1 + set save_spawn_id $spawn_id + spawn telnet $hostname kerberos_master + expect { + {Connection refused} { + close -i $save_spawn_id + wait -i $save_spawn_id + close + wait + continue + } + {Connected} { + send "close\n" + close + wait + set spawn_id $save_spawn_id + break + } + default { + close -i $save_spawn_id + wait -i $save_spawn_id + catch {close} + wait + continue + } + } + } else { + break + } + } + + if {$pids == {}} { + # Try twice to find the server processes. Not sure why, + # but there seems to be some sort of race condition in the OS. + + verbose "server_start: couldn't find server process(es) -- trying again" + exec $sleep 1 + set pids [server_pids] + } + + if {$num_tries > $max_tries} { + myfail "$name: couldn't connect to V4 kpasswd server" + return 0 + } else { + if {$pids != {}} { + verbose "server_start: server process ID(s) is/are $pids" + } + pass "$name" + return 1 + } +} + +proc exp_prog { name prog cmdline status args } { + global spawn_id spawn_pid + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "$name: spawning $prog $cmdline" 1 + + set spawn_pid [eval "spawn $prog $cmdline"] + + # at the end, eof is success + +# lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + lappend args { eof {} } + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + $test + timeout { close; myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { return 0 } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 2 + + if {$status == -1} { return 1 } + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } + + return 1 +} + +proc fix_salt { name fullname oldpw newpw } { + global kdb5_edit + + exp_prog "$name: kdb5_edit" $kdb5_edit "" 0 { + "kdb5_edit:" { send "cpw $fullname\n" } + } { + "Enter password:" { send "$newpw\n" } + } { + "Re-enter password for verification:" { send "$newpw\n" } + } { + "kdb5_edit:" { send "quit\n" } + } +} + +proc unexpire { name fullname } { + global kadmin_local + + # While we're at it, make sure they aren't expired. + exp_prog "$name: kadmin.local" $kadmin_local "" 0 { + "kadmin.local:" { + send "modprinc -expire \"May 6, 1999\" $fullname\n" + } + } { + -re "Principal .* modified." { send "quit\n" } + } +} + +proc kpasswd_v4 { name fullname status oldpw newpw args } { + global kpasswd_v4 s + + eval [concat { + exp_prog $name $kpasswd_v4 "-u $fullname" $status { + -re "Old password for $fullname:" { send "$oldpw\n" } + } { + -re "New Password for $fullname:" { send "$newpw\n" } + } { + -re "Verifying, please re-enter New Password for $fullname:" + { send "$newpw\n" } + } + } $args] +} diff --git a/src/kadmin/v4server/unit-test/remove_changepw_perms.sh b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh new file mode 100644 index 000000000..27d026ff3 --- /dev/null +++ b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +ed /krb5/ovsec_adm.acl </dev/null 2>&1 +g/changepw\/kerberos/s/^/#/ +w +q +EOF diff --git a/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp new file mode 100644 index 000000000..3c8e181b2 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp @@ -0,0 +1,11 @@ +load_lib "helpers.exp" + +set timeout 10 + +exp_prog "setup" $ovsec_edit_keytab \ + "-k /krb5/ovsec_adm.srvtab -a -c -p admin changepw/kerberos" \ + 0 { + "Enter password:" { send "admin\n" } +} { + -re "Entry for principal changepw/kerberos .* added to keytab" {} +} diff --git a/src/kadmin/v4server/unit-test/v4server.1/access.exp b/src/kadmin/v4server/unit-test/v4server.1/access.exp new file mode 100644 index 000000000..4d30fc9c7 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/access.exp @@ -0,0 +1,88 @@ +load_lib "helpers.exp" + +set timeout 30 + +# Setup: make sure the principals we will use have V4 salt +fix_salt "A.setup" testuser notathena notathena +unexpire "A.setup" testuser +unexpire "A.setup" changepw/kerberos + +proc kill_admin_server {} { + global env kill getpid + + set pid [exec $getpid kadmind] + if {$pid != ""} { + exec $kill $pid + } +} + +proc start_admin_server {} { + global ovsec_adm_server sleep + + set max_tries 60 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "$ovsec_adm_server couldn't bind; retrying ($num_tries so far)" + } + if {[catch "exec $ovsec_adm_server" msg]} { + if {[regexp {Address already in use} $msg]} { + continue + } + fail "starting $ovsec_adm_server: $msg" + } + return + } + fail "starting $ovsec_adm_server: $msg" +} + +proc remove_changepw_perms {} { + global remove_changepw_perms + + exec $remove_changepw_perms +} + +proc set_changepw_perms { perms } { + remove_changepw_perms + + exec echo "changepw/kerberos@SECURE-TEST.OV.COM $perms" \ + >> /krb5/ovsec_adm.acl +} + +# start off with a dead admin server +kill_admin_server + +set_changepw_perms "i" +start_admin_server +server_start A.1 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.1 testuser 2 notathena foobar { + "Operation requires ``change-password'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.1 -1 +kill_admin_server + +set_changepw_perms "c" +start_admin_server +server_start A.2 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.2 testuser 2 notathena foobar { + "Operation requires ``get'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.2 -1 +kill_admin_server + +set_changepw_perms "ci" + +start_admin_server diff --git a/src/kadmin/v4server/unit-test/v4server.1/change-password.exp b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp new file mode 100644 index 000000000..62b9ec30a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp @@ -0,0 +1,59 @@ +load_lib "helpers.exp" + +set timeout 30 + +spawn stty -a +expect { eof {} } +wait + +# Setup: make sure the principals we will use have V4 salt +fix_salt "CPW.setup" testuser notathena notathena +fix_salt "CPW.setup" pol1 pol111111 pol111111 +fix_salt "CPW.setup" pol2 pol222222 pol222222 +unexpire "CPW.setup" testuser +unexpire "CPW.setup" pol1 +unexpire "CPW.setup" pol2 +unexpire "CPW.setup" changepw/kerberos + +server_start "CPW.all" "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} + +kpasswd_v4 CPW.1 testuser 0 notathena foobar { "Password changed." {} } +kpasswd_v4 CPW.1 testuser 0 foobar notathena { "Password changed." {} } + +kpasswd_v4 CPW.3 pol1 -1 pol111111 foo { + "New password is too short." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.4 pol1 -1 pol111111 foooooooo { + "New password does not have enough character classes." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.5 pol1 -1 pol111111 Abyssinia { + "New password was found in a dictionary" {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.6.setup pol1 0 pol111111 polAAAAAA { "Password changed." {} } +kpasswd_v4 CPW.6 pol1 -1 polAAAAAA pol111111 { + "New password was used previously." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +# this relies on the fact that kdb5_edit resets last_pwd_change, which +# it appears to +kpasswd_v4 CPW.7.setup pol2 0 pol222222 polBBBBBB { "Password changed." {} } +kpasswd_v4 CPW.7 pol2 -1 polBBBBBB pol222222 { + "Password cannot be changed because it was changed too recently." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +server_exit "CPW.all" -1 diff --git a/src/kadmin/v4server/unit-test/v4server.1/usage.exp b/src/kadmin/v4server/unit-test/v4server.1/usage.exp new file mode 100644 index 000000000..4d292067a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/usage.exp @@ -0,0 +1,26 @@ +load_lib "helpers.exp" + +set timeout 10 + +server_start "U.1: -h" "-h" 0 { + -re {Usage: .*} {} +} { + eof {} +} +server_exit "U.1: -h" 255 + +server_start "U.4: -n" "-n" 1 { + "Enter KDC database master key:" { + myfail "unexpected password prompt" + } + "KADM Server starting in the OVSEC_KADM mode" {} +} + +server_exit "U.4: -n" -1 + +server_start "U.5: no -n" "" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} { + "Enter KDC database master key:" { send "mrroot\n" } +} +server_exit "U.5: no -n" -1 diff --git a/src/kdc/ChangeLog b/src/kdc/ChangeLog index 94090314e..eca2edc69 100644 --- a/src/kdc/ChangeLog +++ b/src/kdc/ChangeLog @@ -7,6 +7,10 @@ Sun Jun 9 23:03:06 1996 Ezra Peisach * main.c (finish_realm): Do not invoke krb5_finish_key if encryption block is not set. +Sun May 12 01:17:05 1996 Marc Horowitz + + * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY + Tue May 7 18:19:59 1996 Ken Raeburn Thu May 2 22:52:56 1996 Mark Eichin diff --git a/src/kdc/configure.in b/src/kdc/configure.in index 1e4211d89..163060e72 100644 --- a/src/kdc/configure.in +++ b/src/kdc/configure.in @@ -31,7 +31,7 @@ if test "$withval" = yes; then AC_DEFINE(KRBCONF_KDC_MODIFIES_KDB) fi dnl -USE_KADM_LIBRARY +USE_KADMSRV_LIBRARY USE_KDB5_LIBRARY USE_KRB4_LIBRARY KRB5_LIBRARIES diff --git a/src/krb524/ChangeLog b/src/krb524/ChangeLog index 999353451..f8304f252 100644 --- a/src/krb524/ChangeLog +++ b/src/krb524/ChangeLog @@ -1,3 +1,11 @@ +Fri Jul 19 20:22:47 1996 Marc Horowitz + + * configure.in: added AC_PROG_AWK and USE_GSSAPI_LIBRARY + +Tue Jul 9 16:14:33 1996 Barry Jaspan + + * krb524d.c: use kadm5 instead of kdb + Tue Jul 9 07:16:39 1996 Ezra Peisach * test.c (krb4_print_ticket): Change addr to unsigned KRB4_32 from diff --git a/src/krb524/configure.in b/src/krb524/configure.in index 1f456b3b6..fe92f9bca 100644 --- a/src/krb524/configure.in +++ b/src/krb524/configure.in @@ -4,9 +4,13 @@ AC_PROG_ARCHIVE AC_PROG_ARCHIVE_ADD AC_PROG_RANLIB AC_PROG_INSTALL -AC_TYPE_SIGNAL AC_PROG_AWK -USE_KADM_LIBRARY +AC_TYPE_SIGNAL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_DB_LIBRARY USE_KDB5_LIBRARY USE_KRB4_LIBRARY KRB5_LIBRARIES diff --git a/src/krb524/krb524d.c b/src/krb524/krb524d.c index 1a9dbabca..c765ab7ad 100644 --- a/src/krb524/krb524d.c +++ b/src/krb524/krb524d.c @@ -20,8 +20,9 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include "k5-int.h" -#include "com_err.h" +#include +#include +#include #include #ifdef HAVE_SYS_SELECT_H @@ -36,7 +37,6 @@ #include #include -#include "adm.h" #include #include "krb524.h" @@ -47,16 +47,12 @@ char *whoami; int signalled = 0; static int debug = 0; +void *handle; -int use_keytab; +int use_keytab, use_master; char *keytab = NULL; krb5_keytab kt; -int use_master; -krb5_principal master_princ; -krb5_encrypt_block master_encblock; -krb5_keyblock master_keyblock; - void init_keytab(), init_master(), cleanup_and_exit(); krb5_error_code do_connection(), lookup_service_key(), kdc_get_server_key(); @@ -73,6 +69,8 @@ RETSIGTYPE request_exit(signo) signalled = 1; } +#if 0 +/* this is in the kadm5 library */ int krb5_free_keyblock_contents(context, key) krb5_context context; krb5_keyblock *key; @@ -81,6 +79,7 @@ int krb5_free_keyblock_contents(context, key) krb5_xfree(key->contents); return 0; } +#endif int main(argc, argv) int argc; @@ -92,8 +91,7 @@ int main(argc, argv) int ret, s; fd_set rfds; krb5_context context; - krb5_realm_params *rparams; - char *realm = 0; + krb5_init_context(&context); krb524_init_ets(context); @@ -119,16 +117,12 @@ int main(argc, argv) signal(SIGINT, request_exit); signal(SIGHUP, request_exit); signal(SIGTERM, request_exit); - if (!realm&&(ret = krb5_get_default_realm(context, &realm))) - { -com_err(whoami, ret, "Getting default realm"); -exit(1); -} if (use_keytab) init_keytab(context); if (use_master) - init_master(context); + /* someday maybe there will be some config param options */ + init_master(context, NULL); memset((char *) &saddr, 0, sizeof(struct sockaddr_in)); saddr.sin_family = AF_INET; @@ -161,7 +155,7 @@ exit(1); cleanup_and_exit(0, context); else if (ret == 0) { if (use_master) { - ret = krb5_db_fini(context); + ret = kadm5_flush(handle); if (ret && ret != KRB5_KDB_DBNOTINITED) { com_err(whoami, ret, "closing kerberos database"); cleanup_and_exit(1, context); @@ -188,11 +182,8 @@ void cleanup_and_exit(ret, context) krb5_context context; { if (use_master) { - krb5_finish_key(context, &master_encblock); - memset((char *)&master_encblock, 0, sizeof(master_encblock)); - (void) krb5_db_fini(context); + (void) kadm5_destroy(handle); } - if (use_master) krb5_free_principal(context, master_princ); if (use_keytab) krb5_kt_close(context, kt); krb5_free_context(context); exit(ret); @@ -218,83 +209,20 @@ void init_keytab(context) use_keytab = 1; /* now safe to close keytab */ } -void init_master(context) +void init_master(context, params) krb5_context context; + kadm5_config_params *params; { int ret; - krb5_realm_params *rparams; - char *realm = 0; - char *key_name =0, *dbname = 0; - char *stash_file = 0; use_master = 0; - /* Use the stashed enctype */ - master_keyblock.enctype = ENCTYPE_UNKNOWN; - - if (!realm&&(ret = krb5_get_default_realm(context, &realm))) { - com_err(whoami, ret, "getting default realm"); + if (ret = kadm5_init(whoami, NULL, KADM5_ADMIN_SERVICE, params, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, + &handle)) { + com_err(whoami, ret, "initializing kadm5 library"); cleanup_and_exit(1, context); } - if ((ret = krb5_read_realm_params(context, - realm, - (char *) NULL, (char *) NULL, - &rparams))) { - com_err(whoami, ret, "Reading KDC profile"); -krb5_xfree(realm); - cleanup_and_exit(1,context); - } - - /* Get the value for the database */ - if (rparams->realm_dbname && !dbname) - dbname = strdup(rparams->realm_dbname); - - /* Get the value for the master key name */ - if (rparams->realm_mkey_name && !key_name) - key_name = strdup(rparams->realm_mkey_name); - - /* Get the value for the master key type */ - if (rparams->realm_enctype_valid ) - master_keyblock.enctype = rparams->realm_enctype; - - /* Get the value for the stashfile */ - if (rparams->realm_stash_file) - stash_file = strdup(rparams->realm_stash_file); - - if ((ret = krb5_db_set_name(context, dbname))) { - com_err(whoami, ret, "Setting database name"); - cleanup_and_exit(1,context); - } - - if ((ret = krb5_db_setup_mkey_name(context, key_name, realm, (char **) 0, - &master_princ))) { - free(realm); - com_err(whoami, ret, "while setting up master key name"); - cleanup_and_exit(1, context); - } else { - free(realm); - } - - - if ((ret = krb5_db_fetch_mkey(context, master_princ, &master_encblock, - FALSE, /* non-manual type-in */ - FALSE, /* irrelevant, given prev. arg */ - stash_file, - 0, &master_keyblock))) { - com_err(whoami, ret, "while fetching master key"); - cleanup_and_exit(1, context); - } - - if ((ret = krb5_db_init(context))) { - com_err(whoami, ret, "while initializing master database"); - cleanup_and_exit(1, context); - } - if ((ret = krb5_process_key(context, &master_encblock, - &master_keyblock))) { - krb5_db_fini(context); - com_err(whoami, ret, "while processing master key"); - cleanup_and_exit(1, context); - } - use_master = 1; /* now safe to finish master key */ + use_master = 1; /* now safe to close kadm5 */ } krb5_error_code do_connection(s, context) @@ -434,14 +362,11 @@ krb5_error_code lookup_service_key(context, p, ktype, key) memcpy(key, (char *) &entry.key, sizeof(krb5_keyblock)); return 0; } else if (use_master) { - if ((ret = krb5_db_init(context))) - return ret; return kdc_get_server_key(context, p, key, NULL, ktype); } return 0; } -/* taken from kdc/kdc_util.c, and modified somewhat */ krb5_error_code kdc_get_server_key(context, service, key, kvno, ktype) krb5_context context; krb5_principal service; @@ -450,54 +375,36 @@ krb5_error_code kdc_get_server_key(context, service, key, kvno, ktype) krb5_enctype ktype; { krb5_error_code ret; - int nprincs; - krb5_db_entry server; - krb5_boolean more; - int i, vno, ok_key; - krb5_key_data *pkey; - nprincs = 1; - if ((ret = krb5_db_get_principal(context, service, &server, - &nprincs, &more))) - return(ret); - - if (more) { - krb5_db_free_principal(context, &server, nprincs); - return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); - } else if (nprincs != 1) { - krb5_db_free_principal(context, &server, nprincs); - return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); - } -/* We use krb5_dbe_find_enctype twice because - * in the case of a ENCTYPE_DES_CBC_CRC key, we prefer to find a krb4 - * salt type over a normal key.. Note this may create a problem if the - * server key is passworded and has both a normal and v4 salt. There is - * no good solution to this.*/ + kadm5_principal_ent_rec server; - if (krb5_dbe_find_enctype(context, - &server, - ktype, - (ktype == ENCTYPE_DES_CBC_CRC)? - KRB5_KDB_SALTTYPE_V4:-1, - -1, - &pkey) && - krb5_dbe_find_enctype(context, - &server, - ktype, - -1, - -1, - &pkey)) - { - krb5_db_free_principal(context, &server, nprincs); - return (KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); - } -if (kvno) - *kvno = pkey->key_data_kvno; - ret = krb5_dbekd_decrypt_key_data(context, &master_encblock, - pkey, key, NULL); - krb5_db_free_principal(context, &server, nprincs); - - + if (ret = kadm5_get_principal(handle, service, &server, + KADM5_KEY_DATA)) + return ret; + + /* + * We try kadm5_decrypt_key twice because in the case of a + * ENCTYPE_DES_CBC_CRC key, we prefer to find a krb4 salt type + * over a normal key. Note this may create a problem if the + * server key is passworded and has both a normal and v4 salt. + * There is no good solution to this. + */ + if ((ret = kadm5_decrypt_key(handle, + &server, + ktype, + (ktype == ENCTYPE_DES_CBC_CRC) ? + KRB5_KDB_SALTTYPE_V4 : -1, + -1, + key, NULL, kvno)) && + (ret = kadm5_decrypt_key(handle, + &server, + ktype, + -1, + -1, + key, NULL, kvno))) { + kadm5_free_principal_ent(handle, &server); + return (KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); + } + kadm5_free_principal_ent(handle, &server); return ret; } - diff --git a/src/lib/ChangeLog b/src/lib/ChangeLog index 210d8cf51..c7e7fb6b1 100644 --- a/src/lib/ChangeLog +++ b/src/lib/ChangeLog @@ -12,6 +12,10 @@ Wed Jul 10 20:32:22 1996 Theodore Y. Ts'o timebomb and version server code was moved to krb5_win_do_init(), which is called by krb5_init_context(). +Tue Jul 9 17:31:57 1996 Marc Horowitz + + * configure.in (CONFIG_DIRS): add rpc subdir + Tue Jul 9 16:44:22 1996 Theodore Ts'o * win_glue.c: Add a quick hack so we can time-bomb the libkrb5.dll diff --git a/src/lib/configure.in b/src/lib/configure.in index aebd96c6a..80c359eb3 100644 --- a/src/lib/configure.in +++ b/src/lib/configure.in @@ -7,7 +7,7 @@ else AC_MSG_RESULT(skipping Kerberos 4 libraries) krb4= fi -CONFIG_DIRS(crypto krb5 des425 $krb4 krb5util kdb gssapi kadm) +CONFIG_DIRS(crypto krb5 des425 $krb4 krb5util kdb gssapi rpc kadm5) AC_PROG_ARCHIVE AC_PROG_RANLIB DO_SUBDIRS diff --git a/src/lib/crypto/des/ChangeLog b/src/lib/crypto/des/ChangeLog index f8637a6e7..c6f707290 100644 --- a/src/lib/crypto/des/ChangeLog +++ b/src/lib/crypto/des/ChangeLog @@ -1,3 +1,4 @@ +<<<<<<< ChangeLog Sat Jun 15 03:51:19 1996 Ezra Peisach * Makefile.in (clean): Add space before \ @@ -66,6 +67,11 @@ Thu May 2 18:29:01 1996 Richard Basch [3-DES uses DES3-CBC-CRC to increment a 192 bit sequence number, instead of being only as secure as DES.] +Wed Apr 17 19:25:01 1996 Marc Horowitz + + * cbc_cksum.c (mit_des_cbc_checksum): don't allocate the checksum + contents. The caller is supposed to do this. + Wed Apr 10 17:46:40 1996 Theodore Y. Ts'o * Makefile.in (SRCS,OBJS): Added afsstring2key.c to the list of diff --git a/src/lib/crypto/des/cbc_cksum.c b/src/lib/crypto/des/cbc_cksum.c index 9c98263a2..c4fb8c086 100644 --- a/src/lib/crypto/des/cbc_cksum.c +++ b/src/lib/crypto/des/cbc_cksum.c @@ -86,7 +86,6 @@ mit_des_cbc_checksum(in, in_length, key, key_size, cksum) cksum->checksum_type = CKSUMTYPE_DESCBC; cksum->length = sizeof(mit_des_cblock); - mit_des_cbc_cksum(in, cksum->contents, in_length, schedule, key); cleanup(); diff --git a/src/lib/gssapi/ChangeLog b/src/lib/gssapi/ChangeLog index 0ad7c89bd..036bd4693 100644 --- a/src/lib/gssapi/ChangeLog +++ b/src/lib/gssapi/ChangeLog @@ -12,6 +12,11 @@ Mon May 6 21:33:25 1996 Ezra Peisach * Makefile.in (clean-unix): Remove libgssapi_krb5.stamp. +Wed Apr 17 21:48:15 1996 Marc Horowitz + + * Makefile.in, configure.in: Nothing in mechglue is used anymore, + for now. + Tue Feb 27 22:10:48 1996 Theodore Y. Ts'o * Makefile.in (all-windows, clean-windows): Add mechglue to the diff --git a/src/lib/gssapi/Makefile.in b/src/lib/gssapi/Makefile.in index c27788dc2..b874a951c 100644 --- a/src/lib/gssapi/Makefile.in +++ b/src/lib/gssapi/Makefile.in @@ -10,8 +10,8 @@ KRB5_VER=@KRB5_SH_VERS@ DEPLIBS=$(TOPLIBD)/libcrypto.$(SHEXT).$(CRYPTO_VER) \ $(TOPLIBD)/libcom_err.$(SHEXT).$(COMERR_VER) \ $(TOPLIBD)/libkrb5.$(SHEXT).$(KRB5_VER) -LIB_SUBDIRS= generic krb5 mechglue -LIBDONE= generic/DONE krb5/DONE mechglue/DONE +LIB_SUBDIRS= generic krb5 +LIBDONE= generic/DONE krb5/DONE # mechglue/DONE LIBUPDATE= $(BUILDTOP)/util/libupdate SHLIB_LIBS=-lkrb5 -lcrypto -lcom_err @@ -51,9 +51,9 @@ all-windows:: cd ..\krb5 @echo Making in gssapi\krb5 -$(MAKE) -$(MFLAGS) - cd ..\mechglue - @echo Making in gssapi\mechglue - -$(MAKE) -$(MFLAGS) +# cd ..\mechglue +# @echo Making in gssapi\mechglue +# -$(MAKE) -$(MFLAGS) cd .. clean-windows:: @@ -63,9 +63,9 @@ clean-windows:: cd ..\krb5 @echo Making clean in gssapi\krb5 -$(MAKE) -$(MFLAGS) clean - cd ..\mechglue - @echo Making clean in gssapi\mechglue - -$(MAKE) -$(MFLAGS) clean +# cd ..\mechglue +# @echo Making clean in gssapi\mechglue +# -$(MAKE) -$(MFLAGS) clean cd .. @echo Making clean in gssapi diff --git a/src/lib/gssapi/configure.in b/src/lib/gssapi/configure.in index ee150e11f..164582c64 100644 --- a/src/lib/gssapi/configure.in +++ b/src/lib/gssapi/configure.in @@ -1,6 +1,7 @@ AC_INIT(configure.in) CONFIG_RULES -CONFIG_DIRS(generic krb5 mechglue) +CONFIG_DIRS(generic krb5) +dnl CONFIG_DIRS(generic krb5 mechglue) AC_PROG_ARCHIVE AC_PROG_ARCHIVE_ADD AC_PROG_RANLIB diff --git a/src/lib/gssapi/generic/ChangeLog b/src/lib/gssapi/generic/ChangeLog index d43459914..a3b2a1411 100644 --- a/src/lib/gssapi/generic/ChangeLog +++ b/src/lib/gssapi/generic/ChangeLog @@ -28,6 +28,26 @@ Wed Jun 12 00:46:41 1996 Theodore Ts'o all uses of INTERFACE in favor of KRB5_CALLCONV and KRB5_DLLIMP. +Sun Apr 21 03:07:02 1996 Marc Horowitz + + * gssapi_generic.c, release_buffer.c, release_oid_set.c: added + files which should have been added before, but either I or commit + was confused. + +Wed Apr 17 20:59:23 1996 Marc Horowitz + + * oid_ops.c: moved from mechglue + + * util_canonhost.c (g_canonicalize_host): cast the return value of + malloc() + + * gssapiP_generic.h: Added prototypes for oid_ops.c + + * gssapi.h: Make the types of OM_uint32 constants portable, + fix some minor compile-time nits + + * Makefile.in: change the list of files which need to be built + Tue Apr 2 15:31:25 1996 Theodore Y. Ts'o * Makefile.in (SRCS): Inlined list of source files for SRCS and diff --git a/src/lib/gssapi/generic/Makefile.in b/src/lib/gssapi/generic/Makefile.in index 9c90244f2..c9e940962 100644 --- a/src/lib/gssapi/generic/Makefile.in +++ b/src/lib/gssapi/generic/Makefile.in @@ -39,28 +39,40 @@ gssapi_err_generic.c: gssapi_err_generic.et #endif SRCS = \ - $(srcdir)/disp_major_status.c \ $(srcdir)/disp_com_err_status.c \ + $(srcdir)/disp_major_status.c \ + $(srcdir)/gssapi_generic.c \ + $(srcdir)/oid_ops.c \ + $(srcdir)/release_buffer.c \ + $(srcdir)/release_oid_set.c \ $(srcdir)/util_buffer.c \ $(srcdir)/util_canonhost.c \ $(srcdir)/util_dup.c \ $(srcdir)/util_oid.c \ + $(srcdir)/util_ordering.c \ + $(srcdir)/util_set.c \ $(srcdir)/util_token.c \ - $(srcdir)/utl_nohash_validate.c \ + $(srcdir)/util_validate.c \ gssapi_err_generic.c OBJS = \ - disp_major_status.$(OBJEXT) \ disp_com_err_status.$(OBJEXT) \ + disp_major_status.$(OBJEXT) \ + gssapi_generic.$(OBJEXT) \ + oid_ops.$(OBJEXT) \ + release_buffer.$(OBJEXT) \ + release_oid_set.$(OBJEXT) \ util_buffer.$(OBJEXT) \ util_canonhost.$(OBJEXT) \ util_dup.$(OBJEXT) \ util_oid.$(OBJEXT) \ + util_ordering.$(OBJEXT) \ + util_set.$(OBJEXT) \ util_token.$(OBJEXT) \ - utl_nohash_validate.$(OBJEXT) \ + util_validate.$(OBJEXT) \ gssapi_err_generic.$(OBJEXT) -EHDRDIR= $(BUILDTOP)$(S)include$(S)gssapi +EHDRDIR= $(BUILDTOP)/include/gssapi EXPORTED_HEADERS= gssapi.h gssapi_generic.h HDRS= $(ETHDRS) @@ -89,8 +101,8 @@ clean-windows:: # Krb5InstallHeaders($(EXPORTED_HEADERS), $(KRB5_INCDIR)/krb5) install:: @set -x; for f in $(EXPORTED_HEADERS) ; \ - do $(INSTALL_DATA) $(srcdir)$(S)$$f \ - $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi$(S)$$f ; \ + do $(INSTALL_DATA) $(srcdir)/$$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ done depend:: $(ETSRCS) diff --git a/src/lib/gssapi/generic/disp_com_err_status.c b/src/lib/gssapi/generic/disp_com_err_status.c index 79b5fbbe2..c4db91375 100644 --- a/src/lib/gssapi/generic/disp_com_err_status.c +++ b/src/lib/gssapi/generic/disp_com_err_status.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_generic.h" #include "com_err.h" diff --git a/src/lib/gssapi/generic/disp_major_status.c b/src/lib/gssapi/generic/disp_major_status.c index ad0b17f20..4dd91d25f 100644 --- a/src/lib/gssapi/generic/disp_major_status.c +++ b/src/lib/gssapi/generic/disp_major_status.c @@ -23,6 +23,10 @@ #include "gssapiP_generic.h" #include +/* + * $Id$ + */ + /* This code has knowledge of the min and max errors of each type within the gssapi major status */ diff --git a/src/lib/gssapi/generic/gssapi.h b/src/lib/gssapi/generic/gssapi.h index 2b2e6002b..28cc4ca41 100644 --- a/src/lib/gssapi/generic/gssapi.h +++ b/src/lib/gssapi/generic/gssapi.h @@ -131,14 +131,6 @@ typedef unsigned int uid_t; #endif #endif -#ifndef NPROTOTYPE -#if defined(__ultrix) && !defined (__GNUC__) -#define NPROTOTYPE(x) () -#else -#define NPROTOTYPE(x) PROTOTYPE(x) -#endif -#endif - /* * First, include stddef.h to get size_t defined. */ @@ -160,9 +152,14 @@ typedef unsigned int uid_t; #include #endif /* HAVE_XOM_H */ +/* + * $Id$ + */ + /* * First, define the three platform-dependent pointer types. */ + typedef void FAR * gss_name_t; typedef void FAR * gss_cred_id_t; typedef void FAR * gss_ctx_id_t; @@ -305,7 +302,7 @@ typedef int gss_cred_usage_t; * Expiration time of 2^32-1 seconds means infinite lifetime for a * credential or security context */ -#define GSS_C_INDEFINITE 0xffffffffl +#define GSS_C_INDEFINITE ((OM_uint32) 0xfffffffful) /* Major status codes */ @@ -318,9 +315,9 @@ typedef int gss_cred_usage_t; #define GSS_C_CALLING_ERROR_OFFSET 24 #define GSS_C_ROUTINE_ERROR_OFFSET 16 #define GSS_C_SUPPLEMENTARY_OFFSET 0 -#define GSS_C_CALLING_ERROR_MASK 0377l -#define GSS_C_ROUTINE_ERROR_MASK 0377l -#define GSS_C_SUPPLEMENTARY_MASK 0177777l +#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul) +#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul) +#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul) /* * The macros that test status codes for error conditions. Note that the @@ -345,43 +342,51 @@ typedef int gss_cred_usage_t; * Calling errors: */ #define GSS_S_CALL_INACCESSIBLE_READ \ - (1l << GSS_C_CALLING_ERROR_OFFSET) + (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET) #define GSS_S_CALL_INACCESSIBLE_WRITE \ - (2l << GSS_C_CALLING_ERROR_OFFSET) + (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET) #define GSS_S_CALL_BAD_STRUCTURE \ - (3l << GSS_C_CALLING_ERROR_OFFSET) + (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET) /* * Routine errors: */ -#define GSS_S_BAD_MECH (1l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_NAME (2l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_NAMETYPE (3l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_BINDINGS (4l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_STATUS (5l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_SIG (6l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_NO_CRED (7l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_NO_CONTEXT (8l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_DEFECTIVE_TOKEN (9l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_DEFECTIVE_CREDENTIAL (10l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_CREDENTIALS_EXPIRED (11l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_CONTEXT_EXPIRED (12l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_FAILURE (13l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_BAD_QOP (14l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_UNAUTHORIZED (15l << GSS_C_ROUTINE_ERROR_OFFSET) -#define GSS_S_UNAVAILABLE (16l << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_MECH (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_NAME (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_NAMETYPE (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_BINDINGS (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_STATUS (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_SIG (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_NO_CRED (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_NO_CONTEXT (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DEFECTIVE_TOKEN (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DEFECTIVE_CREDENTIAL \ + (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_CREDENTIALS_EXPIRED \ + (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_CONTEXT_EXPIRED \ + (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_FAILURE (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_BAD_QOP (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_UNAUTHORIZED (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_UNAVAILABLE (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET) /* * XXX new functions. Check to get official error number assigments? */ -#define GSS_S_DUPLICATE_ELEMENT (17l << GSS_C_ROUTINE_ERROR_OFFSET) +#define GSS_S_DUPLICATE_ELEMENT \ + (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET) /* * Supplementary info bits: */ -#define GSS_S_CONTINUE_NEEDED (1l << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) -#define GSS_S_DUPLICATE_TOKEN (1l << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) -#define GSS_S_OLD_TOKEN (1l << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) -#define GSS_S_UNSEQ_TOKEN (1l << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) +#define GSS_S_CONTINUE_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) +#define GSS_S_DUPLICATE_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) +#define GSS_S_OLD_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) +#define GSS_S_UNSEQ_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) +/* + * XXX not in the cbindings yet. remove this comment when it is + */ +#define GSS_S_GAP_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4)) /* diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h index a882f6139..e0bb28d44 100644 --- a/src/lib/gssapi/generic/gssapiP_generic.h +++ b/src/lib/gssapi/generic/gssapiP_generic.h @@ -23,7 +23,10 @@ #ifndef _GSSAPIP_GENERIC_H_ #define _GSSAPIP_GENERIC_H_ -#include "k5-int.h" +/* + * $Id$ + */ + #include "gssapi.h" #include "gssapi_err_generic.h" @@ -86,6 +89,14 @@ /** helper functions **/ +typedef struct _g_set *g_set; + +int g_set_init PROTOTYPE((g_set *s)); +int g_set_destroy PROTOTYPE((g_set *s)); +int g_set_entry_add PROTOTYPE((g_set *s, void *key, void *value)); +int g_set_entry_delete PROTOTYPE((g_set *s, void *key)); +int g_set_entry_get PROTOTYPE((g_set *s, void *key, void **value)); + int g_save_name PROTOTYPE((void **vdb, gss_name_t *name)); int g_save_cred_id PROTOTYPE((void **vdb, gss_cred_id_t *cred)); int g_save_ctx_id PROTOTYPE((void **vdb, gss_ctx_id_t *ctx)); @@ -119,8 +130,64 @@ OM_uint32 g_display_com_err_status PROTOTYPE((OM_uint32 *minor_status, OM_uint32 status_value, gss_buffer_t status_string)); -char * g_canonicalize_host PROTOTYPE((char *hostname)); - -char * g_strdup PROTOTYPE((char *str)); +OM_uint32 g_order_init PROTOTYPE((void **queue, unsigned int seqnum, + int do_replay, int do_sequence)); + +OM_uint32 g_order_check PROTOTYPE((void **queue, unsigned int seqnum)); + +void g_order_free PROTOTYPE((void **queue)); + +char *g_canonicalize_host PROTOTYPE((char *hostname)); +char *g_local_host_name PROTOTYPE((void)); + +char *g_strdup PROTOTYPE((char *str)); + +/** declarations of internal name mechanism functions **/ + +OM_uint32 generic_gss_release_buffer +PROTOTYPE((OM_uint32*, /* minor_status */ + gss_buffer_t /* buffer */ + )); + +OM_uint32 generic_gss_release_oid_set +PROTOTYPE((OM_uint32*, /* minor_status */ + gss_OID_set* /* set */ + )); + +OM_uint32 generic_gss_copy_oid +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_OID, /* oid */ + gss_OID * /* new_oid */ + )); + +OM_uint32 generic_gss_create_empty_oid_set +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_OID_set * /* oid_set */ + )); + +OM_uint32 generic_gss_add_oid_set_member +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_OID, /* member_oid */ + gss_OID_set * /* oid_set */ + )); + +OM_uint32 generic_gss_test_oid_set_member +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_OID, /* member */ + gss_OID_set, /* set */ + int * /* present */ + )); + +OM_uint32 generic_gss_oid_to_str +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_OID, /* oid */ + gss_buffer_t /* oid_str */ + )); + +OM_uint32 generic_gss_str_to_oid +PROTOTYPE( (OM_uint32 *, /* minor_status */ + gss_buffer_t, /* oid_str */ + gss_OID * /* oid */ + )); #endif /* _GSSAPIP_GENERIC_H_ */ diff --git a/src/lib/gssapi/generic/gssapi_err_generic.et b/src/lib/gssapi/generic/gssapi_err_generic.et index 91d958d12..ddeed4227 100644 --- a/src/lib/gssapi/generic/gssapi_err_generic.et +++ b/src/lib/gssapi/generic/gssapi_err_generic.et @@ -20,6 +20,10 @@ # PERFORMANCE OF THIS SOFTWARE. # +# +# $Id$ +# + error_table ggss error_code G_BAD_SERVICE_NAME, "No @ in SERVICE-NAME name string" @@ -31,5 +35,9 @@ error_code G_BAD_MSG_CTX, "Message context invalid" error_code G_WRONG_SIZE, "Buffer is the wrong size" error_code G_BAD_USAGE, "Credential usage type is unknown" error_code G_UNKNOWN_QOP, "Unknown quality of protection specified" +error_code G_NO_HOSTNAME, "Local host name could not be determined" error_code G_BAD_HOSTNAME, "Hostname in SERVICE-NAME string could not be canonicalized" +error_code G_WRONG_MECH, "Mechanism is incorrect" +error_code G_BAD_TOK_HEADER, "Token header is malformed or corrupt" +error_code G_BAD_DIRECTION, "Packet was replayed in wrong direction" end diff --git a/src/lib/gssapi/generic/gssapi_generic.c b/src/lib/gssapi/generic/gssapi_generic.c new file mode 100644 index 000000000..7072329b7 --- /dev/null +++ b/src/lib/gssapi/generic/gssapi_generic.c @@ -0,0 +1,59 @@ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_generic.h" + +/* + * See krb5/gssapi_krb5.c for a description of the algorithm for + * encoding an object identifier. + */ + +/* + * The OID of user_name is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * generic(1) user_name(1) = 1.2.840.113554.1.2.1.1 + * machine_uid_name: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * generic(1) machine_uid_name(2) = 1.2.840.113554.1.2.1.2 + * string_uid_name: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * generic(1) string_uid_name(3) = 1.2.840.113554.1.2.1.3 + * service_name: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * generic(1) service_name(4) = 1.2.840.113554.1.2.1.4 + */ + +static gss_OID_desc oids[] = { + {10, "\052\206\110\206\367\022\001\002\001\001"}, + {10, "\052\206\110\206\367\022\001\002\001\002"}, + {10, "\052\206\110\206\367\022\001\002\001\003"}, + {10, "\052\206\110\206\367\022\001\002\001\004"}, +}; + +gss_OID gss_nt_user_name = oids+0; +gss_OID gss_nt_machine_uid_name = oids+1; +gss_OID gss_nt_string_uid_name = oids+2; +gss_OID gss_nt_service_name = oids+3; diff --git a/src/lib/gssapi/generic/gssapi_generic.h b/src/lib/gssapi/generic/gssapi_generic.h index fe2282796..88c054788 100644 --- a/src/lib/gssapi/generic/gssapi_generic.h +++ b/src/lib/gssapi/generic/gssapi_generic.h @@ -23,6 +23,10 @@ #ifndef _GSSAPI_GENERIC_H_ #define _GSSAPI_GENERIC_H_ +/* + * $Id$ + */ + #if defined(__MWERKS__) || defined(applec) || defined(THINK_C) #include #else diff --git a/src/lib/gssapi/generic/oid_ops.c b/src/lib/gssapi/generic/oid_ops.c new file mode 100644 index 000000000..38d73f429 --- /dev/null +++ b/src/lib/gssapi/generic/oid_ops.c @@ -0,0 +1,385 @@ +/* + * lib/gssapi/generic/oid_ops.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. + * + */ + +/* + * oid_ops.c - GSS-API V2 interfaces to manipulate OIDs + */ + +#include "gssapiP_generic.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#if 0 +OM_uint32 +generic_gss_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + *minor_status = 0; + + if (*oid == GSS_C_NO_OID) + return(GSS_S_COMPLETE); + + /* + * The V2 API says the following! + * + * gss_release_oid[()] will recognize any of the GSSAPI's own OID values, + * and will silently ignore attempts to free these OIDs; for other OIDs + * it will call the C free() routine for both the OID data and the + * descriptor. This allows applications to freely mix their own heap- + * allocated OID values with OIDs returned by GSS-API. + */ + if ((*oid != gss_nt_user_name) && + (*oid != gss_nt_machine_uid_name) && + (*oid != gss_nt_string_uid_name) && + (*oid != gss_nt_service_name)) { + free((*oid)->elements); + free(*oid); + } + *oid = GSS_C_NO_OID; + return(GSS_S_COMPLETE); +} +#endif + +OM_uint32 +generic_gss_copy_oid(minor_status, oid, new_oid) + OM_uint32 *minor_status; + gss_OID oid, *new_oid; +{ + gss_OID p; + + p = (gss_OID) malloc(sizeof(gss_OID_desc)); + if (!p) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + p->length = oid->length; + p->elements = malloc(p->length); + if (!p->elements) { + free(p); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + memcpy(p->elements, oid->elements, p->length); + *new_oid = p; + return(GSS_S_COMPLETE); +} + + +OM_uint32 +generic_gss_create_empty_oid_set(minor_status, oid_set) + OM_uint32 *minor_status; + gss_OID_set *oid_set; +{ + if ((*oid_set = (gss_OID_set) malloc(sizeof(gss_OID_set_desc)))) { + memset(*oid_set, 0, sizeof(gss_OID_set_desc)); + *minor_status = 0; + return(GSS_S_COMPLETE); + } + else { + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } +} + +OM_uint32 +generic_gss_add_oid_set_member(minor_status, member_oid, oid_set) + OM_uint32 *minor_status; + gss_OID member_oid; + gss_OID_set *oid_set; +{ + gss_OID elist; + gss_OID lastel; + + elist = (*oid_set)->elements; + /* Get an enlarged copy of the array */ + if (((*oid_set)->elements = (gss_OID) malloc(((*oid_set)->count+1) * + sizeof(gss_OID_desc)))) { + /* Copy in the old junk */ + if (elist) + memcpy((*oid_set)->elements, + elist, + ((*oid_set)->count * sizeof(gss_OID_desc))); + + /* Duplicate the input element */ + lastel = &(*oid_set)->elements[(*oid_set)->count]; + if ((lastel->elements = + (void *) malloc((size_t) member_oid->length))) { + /* Success - copy elements */ + memcpy(lastel->elements, member_oid->elements, + (size_t) member_oid->length); + /* Set length */ + lastel->length = member_oid->length; + + /* Update count */ + (*oid_set)->count++; + if (elist) + free(elist); + *minor_status = 0; + return(GSS_S_COMPLETE); + } + else + free((*oid_set)->elements); + } + /* Failure - restore old contents of list */ + (*oid_set)->elements = elist; + *minor_status = ENOMEM; + return(GSS_S_FAILURE); +} + +OM_uint32 +generic_gss_test_oid_set_member(minor_status, member, set, present) + OM_uint32 *minor_status; + gss_OID member; + gss_OID_set set; + int *present; +{ + size_t i; + int result; + + result = 0; + for (i=0; icount; i++) { + if ((set->elements[i].length == member->length) && + !memcmp(set->elements[i].elements, + member->elements, + (size_t) member->length)) { + result = 1; + break; + } + } + *present = result; + *minor_status = 0; + return(GSS_S_COMPLETE); +} + +/* + * OID<->string routines. These are uuuuugly. + */ +OM_uint32 +generic_gss_oid_to_str(minor_status, oid, oid_str) + OM_uint32 *minor_status; + gss_OID oid; + gss_buffer_t oid_str; +{ + char numstr[128]; + unsigned long number; + int numshift; + size_t string_length; + size_t i; + unsigned char *cp; + char *bp; + + /* Decoded according to krb5/gssapi_krb5.c */ + + /* First determine the size of the string */ + string_length = 0; + number = 0; + numshift = 0; + cp = (unsigned char *) oid->elements; + number = (unsigned long) cp[0]; + sprintf(numstr, "%ld ", number/40); + string_length += strlen(numstr); + sprintf(numstr, "%ld ", number%40); + string_length += strlen(numstr); + for (i=1; ilength; i++) { + if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { + number = (number << 7) | (cp[i] & 0x7f); + numshift += 7; + } + else { + *minor_status = EINVAL; + return(GSS_S_FAILURE); + } + if ((cp[i] & 0x80) == 0) { + sprintf(numstr, "%ld ", number); + string_length += strlen(numstr); + number = 0; + numshift = 0; + } + } + /* + * If we get here, we've calculated the length of "n n n ... n ". Add 4 + * here for "{ " and "}\0". + */ + string_length += 4; + if ((bp = (char *) malloc(string_length))) { + strcpy(bp, "{ "); + number = (unsigned long) cp[0]; + sprintf(numstr, "%ld ", number/40); + strcat(bp, numstr); + sprintf(numstr, "%ld ", number%40); + strcat(bp, numstr); + number = 0; + cp = (unsigned char *) oid->elements; + for (i=1; ilength; i++) { + number = (number << 7) | (cp[i] & 0x7f); + if ((cp[i] & 0x80) == 0) { + sprintf(numstr, "%ld ", number); + strcat(bp, numstr); + number = 0; + } + } + strcat(bp, "}"); + oid_str->length = strlen(bp)+1; + oid_str->value = (void *) bp; + *minor_status = 0; + return(GSS_S_COMPLETE); + } + *minor_status = ENOMEM; + return(GSS_S_FAILURE); +} + +OM_uint32 +generic_gss_str_to_oid(minor_status, oid_str, oid) + OM_uint32 *minor_status; + gss_buffer_t oid_str; + gss_OID *oid; +{ + char *cp, *bp, *startp; + int brace; + long numbuf; + long onumbuf; + OM_uint32 nbytes; + int index; + unsigned char *op; + + brace = 0; + bp = (char *) oid_str->value; + cp = bp; + /* Skip over leading space */ + while ((bp < &cp[oid_str->length]) && isspace(*bp)) + bp++; + if (*bp == '{') { + brace = 1; + bp++; + } + while ((bp < &cp[oid_str->length]) && isspace(*bp)) + bp++; + startp = bp; + nbytes = 0; + + /* + * The first two numbers are chewed up by the first octet. + */ + if (sscanf(bp, "%ld", &numbuf) != 1) { + *minor_status = EINVAL; + return(GSS_S_FAILURE); + } + while ((bp < &cp[oid_str->length]) && isdigit(*bp)) + bp++; + while ((bp < &cp[oid_str->length]) && isspace(*bp)) + bp++; + if (sscanf(bp, "%ld", &numbuf) != 1) { + *minor_status = EINVAL; + return(GSS_S_FAILURE); + } + while ((bp < &cp[oid_str->length]) && isdigit(*bp)) + bp++; + while ((bp < &cp[oid_str->length]) && isspace(*bp)) + bp++; + nbytes++; + while (isdigit(*bp)) { + if (sscanf(bp, "%ld", &numbuf) != 1) { + *minor_status = EINVAL; + return(GSS_S_FAILURE); + } + while (numbuf) { + nbytes++; + numbuf >>= 7; + } + while ((bp < &cp[oid_str->length]) && isdigit(*bp)) + bp++; + while ((bp < &cp[oid_str->length]) && isspace(*bp)) + bp++; + } + if (brace && (*bp != '}')) { + *minor_status = EINVAL; + return(GSS_S_FAILURE); + } + + /* + * Phew! We've come this far, so the syntax is good. + */ + if ((*oid = (gss_OID) malloc(sizeof(gss_OID_desc)))) { + if (((*oid)->elements = (void *) malloc((size_t) nbytes))) { + (*oid)->length = nbytes; + op = (unsigned char *) (*oid)->elements; + bp = startp; + sscanf(bp, "%ld", &numbuf); + while (isdigit(*bp)) + bp++; + while (isspace(*bp)) + bp++; + onumbuf = 40*numbuf; + sscanf(bp, "%ld", &numbuf); + onumbuf += numbuf; + *op = (unsigned char) onumbuf; + op++; + while (isdigit(*bp)) + bp++; + while (isspace(*bp)) + bp++; + while (isdigit(*bp)) { + sscanf(bp, "%ld", &numbuf); + nbytes = 0; + /* Have to fill in the bytes msb-first */ + onumbuf = numbuf; + while (numbuf) { + nbytes++; + numbuf >>= 7; + } + numbuf = onumbuf; + op += nbytes; + index = -1; + while (numbuf) { + op[index] = (unsigned char) numbuf & 0x7f; + if (index != -1) + op[index] |= 0x80; + index--; + numbuf >>= 7; + } + while (isdigit(*bp)) + bp++; + while (isspace(*bp)) + bp++; + } + *minor_status = 0; + return(GSS_S_COMPLETE); + } + else { + free(*oid); + *oid = GSS_C_NO_OID; + } + } + *minor_status = ENOMEM; + return(GSS_S_FAILURE); +} + diff --git a/src/lib/gssapi/generic/release_buffer.c b/src/lib/gssapi/generic/release_buffer.c new file mode 100644 index 000000000..d367ef31e --- /dev/null +++ b/src/lib/gssapi/generic/release_buffer.c @@ -0,0 +1,58 @@ +/* #ident "@(#)g_rel_buffer.c 1.2 96/02/06 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_buffer + */ + +#include "gssapiP_generic.h" + +#include +#ifdef HAVE_STDLIB_H +#include +#endif + +OM_uint32 INTERFACE +generic_gss_release_buffer (minor_status, + buffer) + OM_uint32 * minor_status; + gss_buffer_t buffer; +{ + if (minor_status) + *minor_status = 0; + + /* if buffer is NULL, return */ + + if (buffer == GSS_C_NO_BUFFER) + return(GSS_S_COMPLETE); + + if ((buffer->length) && + (buffer->value)) { + free(buffer->value); + buffer->length = 0; + buffer->value = NULL; + } + + return (GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/generic/release_oid_set.c b/src/lib/gssapi/generic/release_oid_set.c new file mode 100644 index 000000000..01e814bcb --- /dev/null +++ b/src/lib/gssapi/generic/release_oid_set.c @@ -0,0 +1,62 @@ +/* #ident "@(#)gss_release_oid_set.c 1.12 95/08/23 SMI" */ + +/* + * Copyright 1996 by Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of Sun Microsystems not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Sun Microsystems makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * glue routine for gss_release_oid_set + */ + +#include "gssapiP_generic.h" + +#include +#ifdef HAVE_STDLIB_H +#include +#endif + +OM_uint32 INTERFACE +generic_gss_release_oid_set (minor_status, + set) + OM_uint32 * minor_status; + gss_OID_set * set; +{ + size_t i; + gss_OID oid; + if (minor_status) + *minor_status = 0; + + if (set == NULL) + return(GSS_S_COMPLETE); + + if (*set == GSS_C_NULL_OID_SET) + return(GSS_S_COMPLETE); + + for (i=0; i<(*set)->count; i++) + free((*set)->elements[i].elements); + + free((*set)->elements); + free(*set); + + *set = GSS_C_NULL_OID_SET; + + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/generic/util_buffer.c b/src/lib/gssapi/generic/util_buffer.c index cf144495f..e715834d9 100644 --- a/src/lib/gssapi/generic/util_buffer.c +++ b/src/lib/gssapi/generic/util_buffer.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_generic.h" #include diff --git a/src/lib/gssapi/generic/util_canonhost.c b/src/lib/gssapi/generic/util_canonhost.c index 896b950b1..900834f40 100644 --- a/src/lib/gssapi/generic/util_canonhost.c +++ b/src/lib/gssapi/generic/util_canonhost.c @@ -20,10 +20,16 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + /* This file could be OS specific */ -#define NEED_SOCKETS + #include "gssapiP_generic.h" +#include "port-sockets.h" + #ifndef _MACINTOSH #include #endif @@ -41,7 +47,7 @@ g_canonicalize_host(hostname) if ((hent = gethostbyname(hostname)) == NULL) return(NULL); - if (! (haddr = xmalloc(hent->h_length))) { + if (! (haddr = (char *) xmalloc(hent->h_length))) { return(NULL); } @@ -53,7 +59,7 @@ g_canonicalize_host(hostname) xfree(haddr); - if ((canon = xmalloc(strlen(hent->h_name)+1)) == NULL) + if ((canon = (char *) xmalloc(strlen(hent->h_name)+1)) == NULL) return(NULL); strcpy(canon, hent->h_name); diff --git a/src/lib/gssapi/generic/util_dup.c b/src/lib/gssapi/generic/util_dup.c index 6b19092db..d601ceef2 100644 --- a/src/lib/gssapi/generic/util_dup.c +++ b/src/lib/gssapi/generic/util_dup.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_generic.h" #include diff --git a/src/lib/gssapi/generic/util_oid.c b/src/lib/gssapi/generic/util_oid.c index 91bb544ed..8843a7ff6 100644 --- a/src/lib/gssapi/generic/util_oid.c +++ b/src/lib/gssapi/generic/util_oid.c @@ -22,6 +22,10 @@ #include "gssapiP_generic.h" +/* + * $Id$ + */ + int g_copy_OID_set(in, out) const gss_OID_set_desc * const in; diff --git a/src/lib/gssapi/generic/util_token.c b/src/lib/gssapi/generic/util_token.c index e440d907a..027d2a765 100644 --- a/src/lib/gssapi/generic/util_token.c +++ b/src/lib/gssapi/generic/util_token.c @@ -23,11 +23,9 @@ #include "gssapiP_generic.h" #include -#if (SIZEOF_INT == 2) -#define VALID_INT_BITS 0x7fff -#elif (SIZEOF_INT == 4) -#define VALID_INT_BITS 0x7fffffff -#endif +/* + * $Id$ + */ /* XXXX this code currently makes the assumption that a mech oid will never be longer than 127 bytes. This assumption is not inherent in @@ -153,58 +151,68 @@ void g_make_token_header(mech, body_size, buf, tok_type) *(*buf)++ = (unsigned char) (tok_type&0xff); } -/* given a buffer containing a token, reads and verifies the token, - leaving buf advanced past the token header, and setting body_size - to the number of remaining bytes */ - -int g_verify_token_header(mech, body_size, buf, tok_type, toksize) +/* + * Given a buffer containing a token, reads and verifies the token, + * leaving buf advanced past the token header, and setting body_size + * to the number of remaining bytes. Returns 0 on success, + * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the + * mechanism in the token does not match the mech argument. buf and + * *body_size are left unmodified on error. + */ +int g_verify_token_header(mech, body_size, buf_in, tok_type, toksize) gss_OID mech; int *body_size; - unsigned char **buf; + unsigned char **buf_in; int tok_type; int toksize; { + char *buf = *buf_in; int seqsize; gss_OID_desc toid; + int ret = 0; if ((toksize-=1) < 0) - return(0); - if (*(*buf)++ != 0x60) - return(0); + return(G_BAD_TOK_HEADER); + if (*buf++ != 0x60) + return(G_BAD_TOK_HEADER); - if ((seqsize = der_read_length(buf, &toksize)) < 0) - return(0); + if ((seqsize = der_read_length(&buf, &toksize)) < 0) + return(G_BAD_TOK_HEADER); if (seqsize != toksize) - return(0); + return(G_BAD_TOK_HEADER); if ((toksize-=1) < 0) - return(0); - if (*(*buf)++ != 0x06) - return(0); + return(G_BAD_TOK_HEADER); + if (*buf++ != 0x06) + return(G_BAD_TOK_HEADER); if ((toksize-=1) < 0) - return(0); - toid.length = *(*buf)++; - - if ((toid.length & VALID_INT_BITS) != toid.length) /* Overflow??? */ - return(0); - if ((toksize-= (int) toid.length) < 0) - return(0); - toid.elements = *buf; - (*buf)+=toid.length; - - if (! g_OID_equal(&toid, mech)) - return(0); + return(G_BAD_TOK_HEADER); + toid.length = *buf++; + + if ((toksize-=toid.length) < 0) + return(G_BAD_TOK_HEADER); + toid.elements = buf; + buf+=toid.length; + + if (! g_OID_equal(&toid, mech)) + ret = G_WRONG_MECH; + /* G_WRONG_MECH is not returned immediately because it's more important + to return G_BAD_TOK_HEADER if the token header is in fact bad */ + if ((toksize-=2) < 0) - return(0); + return(G_BAD_TOK_HEADER); - if ((*(*buf)++ != ((tok_type>>8)&0xff)) || - (*(*buf)++ != (tok_type&0xff))) - return(0); + if ((*buf++ != ((tok_type>>8)&0xff)) || + (*buf++ != (tok_type&0xff))) + return(G_BAD_TOK_HEADER); - *body_size = toksize; + if (!ret) { + *buf_in = buf; + *body_size = toksize; + } - return(1); + return(ret); } diff --git a/src/lib/gssapi/generic/util_validate.c b/src/lib/gssapi/generic/util_validate.c index 72631341b..63f552859 100644 --- a/src/lib/gssapi/generic/util_validate.c +++ b/src/lib/gssapi/generic/util_validate.c @@ -20,6 +20,10 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + /* * functions to validate name, credential, and context handles */ @@ -30,24 +34,31 @@ #include #include #include +#ifdef HAVE_BSD_DB #include -#define V_NAME 1 -#define V_CRED_ID 2 -#define V_CTX_ID 3 +static const int one = 1; +static const DBT dbtone = { (void *) &one, sizeof(one) }; typedef struct _vkey { int type; void *ptr; } vkey; +#endif -static const int one = 1; -static const DBT dbtone = { (void *) &one, sizeof(one) }; +#define V_NAME 1 +#define V_CRED_ID 2 +#define V_CTX_ID 3 /* All these functions return 0 on failure, and non-zero on success */ -static int g_save(DB **vdb, int type, void *ptr) +static int g_save(db, type, ptr) + void **db; + int type; + void *ptr; { +#ifdef HAVE_BSD_DB + DB **vdb = (DB **) db; vkey vk; DBT key; @@ -61,10 +72,24 @@ static int g_save(DB **vdb, int type, void *ptr) key.size = sizeof(vk); return((*((*vdb)->put))(*vdb, &key, &dbtone, 0) == 0); +#else + g_set *gs = (g_set *) db; + + if (!*gs) + if (g_set_init(gs)) + return(0); + + return(g_set_entry_add(gs, ptr, (void *) type) == 0); +#endif } -static int g_validate(DB **vdb, int type, void *ptr) +static int g_validate(db, type, ptr) + void **db; + int type; + void *ptr; { +#ifdef HAVE_BSD_DB + DB **vdb = (DB **) db; vkey vk; DBT key, value; @@ -82,10 +107,27 @@ static int g_validate(DB **vdb, int type, void *ptr) return((value.size == sizeof(one)) && (*((int *) value.data) == one)); +#else + g_set *gs = (g_set *) db; + void *value; + + if (!*gs) + return(0); + + if (g_set_entry_get(gs, ptr, (void **) &value)) + return(0); + + return(((int) value) == type); +#endif } -static int g_delete(DB **vdb, int type, void *ptr) +static int g_delete(db, type, ptr) + void **db; + int type; + void *ptr; { +#ifdef HAVE_BSD_DB + DB **vdb = (DB **) db; vkey vk; DBT key; @@ -99,52 +141,81 @@ static int g_delete(DB **vdb, int type, void *ptr) key.size = sizeof(vk); return((*((*vdb)->del))(*vdb, &key, 0) == 0); +#else + g_set *gs = (g_set *) db; + + if (!*gs) + return(0); + + if (g_set_entry_delete(gs, ptr)) + return(0); + + return(1); +#endif } /* functions for each type */ /* save */ -int g_save_name(void **vdb, gss_name_t *name) +int g_save_name(vdb, name) + void **vdb; + gss_name_t *name; { - return(g_save((DB **) vdb, V_NAME, (void *) name)); + return(g_save(vdb, V_NAME, (void *) name)); } -int g_save_cred_id(void **vdb, gss_cred_id_t *cred) +int g_save_cred_id(vdb, cred) + void **vdb; + gss_cred_id_t *cred; { - return(g_save((DB **) vdb, V_CRED_ID, (void *) cred)); + return(g_save(vdb, V_CRED_ID, (void *) cred)); } -int g_save_ctx_id(void **vdb, gss_ctx_id_t *ctx) +int g_save_ctx_id(vdb, ctx) + void **vdb; + gss_ctx_id_t *ctx; { - return(g_save((DB **) vdb, V_CTX_ID, (void *) ctx)); + return(g_save(vdb, V_CTX_ID, (void *) ctx)); } /* validate */ -int g_validate_name(void **vdb, gss_name_t *name) +int g_validate_name(vdb, name) + void **vdb; + gss_name_t *name; { - return(g_validate((DB **) vdb, V_NAME, (void *) name)); + return(g_validate(vdb, V_NAME, (void *) name)); } -int g_validate_cred_id(void **vdb, gss_cred_id_t *cred) +int g_validate_cred_id(vdb, cred) + void **vdb; + gss_cred_id_t *cred; { - return(g_validate((DB **) vdb, V_CRED_ID, (void *) cred)); + return(g_validate(vdb, V_CRED_ID, (void *) cred)); } -int g_validate_ctx_id(void **vdb, gss_ctx_id_t *ctx) +int g_validate_ctx_id(vdb, ctx) + void **vdb; + gss_ctx_id_t *ctx; { - return(g_validate((DB **) vdb, V_CTX_ID, (void *) ctx)); + return(g_validate(vdb, V_CTX_ID, (void *) ctx)); } /* delete */ -int g_delete_name(void **vdb, gss_name_t *name) +int g_delete_name(vdb, name) + void **vdb; + gss_name_t *name; { - return(g_delete((DB **) vdb, V_NAME, (void *) name)); + return(g_delete(vdb, V_NAME, (void *) name)); } -int g_delete_cred_id(void **vdb, gss_cred_id_t *cred) +int g_delete_cred_id(vdb, cred) + void **vdb; + gss_cred_id_t *cred; { - return(g_delete((DB **) vdb, V_CRED_ID, (void *) cred)); + return(g_delete(vdb, V_CRED_ID, (void *) cred)); } -int g_delete_ctx_id(void **vdb, gss_ctx_id_t *ctx) +int g_delete_ctx_id(vdb, ctx) + void **vdb; + gss_ctx_id_t *ctx; { - return(g_delete((DB **) vdb, V_CTX_ID, (void *) ctx)); + return(g_delete(vdb, V_CTX_ID, (void *) ctx)); } diff --git a/src/lib/gssapi/krb5/ChangeLog b/src/lib/gssapi/krb5/ChangeLog index 73a09c111..a996733e6 100644 --- a/src/lib/gssapi/krb5/ChangeLog +++ b/src/lib/gssapi/krb5/ChangeLog @@ -1,3 +1,43 @@ +Thu Jul 18 19:48:48 1996 Marc Horowitz + + * init_sec_context.c (krb5_gss_init_sec_context), + accept_sec_context.c (krb5_gss_accept_sec_context): ifdef'd out + reference to 3des. + +Fri Jul 5 15:27:29 1996 Marc Horowitz + + * gssapi_krb5.h: Add declarations for _old mech set, and _both + mech set + +Thu Jun 20 23:15:57 1996 Marc Horowitz + + * ser_sctx.c (kg_oid_size, kg_ctx_size): pull the oid-related code + out of kg_ctx_size into kg_oid_size. + + * k5unseal.c (kg_unseal), k5seal.c (make_seal_token): == cannot be + used to compare oid's. The g_OID_equal macro must be used. + + * init_sec_context.c (make_ap_req, krb5_gss_init_sec_context): - + gss_init_sec_context should use the mech set in the credential. + If the default mech is requested, but the old mech oid was + explicitly passed to gss_acquire_cred, then the context should be + the old mech, otherwise, the new mech. If a mech was requested + explicitly, then the code should insure that the credential is + compatible. + + * acquire_cred.c (krb5_gss_acquire_cred), gssapiP_krb5.h (struct + _krb5_gss_cred_it_rec), gssapi_krb5.c (gss_mech_set_krb5*), + inq_cred.c (krb5_gss_inquire_cred): gss_acquire_cred needs to be + able to deal with both mech oid's. It should return in + actual_mechs the intersection of the set passed in and the + {old,new} mechs, or if the default was requested, it should return + both mech oid's. This state should be stored in the credential + handle, and regurgitated by gss_inquire_cred. + + * accept_sec_context.c (krb5_gss_accept_sec_context): make sure + that the oid in the token is compatible with the mechanisms + specified by the credential. + Thu Jun 13 22:11:30 1996 Tom Yu * configure.in: remove ref to ET_RULES @@ -29,6 +69,63 @@ Tue May 14 04:42:11 1996 Theodore Y. Ts'o krb5_auth_con_setcksumtype to use krb5_auth_con_set_req_cksumtype by default instead. +Sun May 12 00:54:35 1996 Marc Horowitz + + * util_crypt.c (kg_encrypt): It used to be that krb5_encrypt could + be used to encrypt in place. That's broken now. This would need + to be fixed in several places in the crypto layer, and it's not + clear what the right thing is, so it's worked around here in the + interests of portability and reliablility, at the expense of a + malloc/memcpy/free. + + * Makefile.in, configure.in: gssapi_krb5.h should be installed + inside the tree. This is really only half the work, as it should + be installed outside of the tree, too. + +Sat Apr 20 00:02:51 1996 Marc Horowitz + + * accept_sec_context.c, export_sec_context.c, gssapiP_krb5.h, + import_sec_context.c, init_sec_context.c, k5seal.c, k5unseal.c, + ser_sctx.c, wrap_size_limit.c: Implemented triple-des changes + based on Richard's patches. + +Wed Apr 17 21:08:59 1996 Marc Horowitz + + * accept_sec_context.c (krb5_gss_set_backward_mode): removed + + * krb5_gss_glue.c, wrap_size_limit.c: added + + * import_sec_context.c: intern the newly created context id so + that the validation functions will accept it. + + * Makefile.in (CFLAGS): Don't need md5 header files anymore. + (OBJS, SRCS): Change the list of files to build. + + * export_sec_context.c, import_sec_context.c, gssapiP_krb5.h, + ser_sctx.c: don't use the serialization abstraction, since it + doesn't add anything, and is internal to kerberos. Instead, make + the {de,}serialization functions internal gssapi functions, and + call those directly. + + * accept_sec_context.c, acquire_cred.c, context_time.c, + delete_sec_context.c, disp_name.c, disp_status.c, + export_sec_context.c, gssapi_krb5.c (kg_get_context), + import_name.c, import_sec_context.c, indicate_mechs.c, + init_sec_context.c, inq_context.c, inq_cred.c, inq_names.c, + process_context_token.c, rel_cred.c, rel_name.c, seal.c, sign.c, + unseal.c, verify.c: + Don't pass in the context from the caller. Instead, call + kg_get_context() to find out the kerberos library context. Also, + random minor compile-time fixes. + + * accept_sec_context.c, gssapi_krb5.c (kg_get_defcred), + gssapiP_krb5.h, init_sec_context.c, k5seal.c, k5unseal.c, + util_cksum.c (kg_checksum_channel_bindings), util_seqnum.c + (kg_make_seq_num, kg_get_seq_num), util_seed.c (kg_make_seed), + util_crypt.c (kg_encrypt, kg_decrypt): + pass the context to the kg_* functions which need it instead of + determining it directly. + Fri Apr 12 21:47:46 1996 Richard Basch * k5seal.c k5unseal.c: diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in index d5061fa14..4a18efeea 100644 --- a/src/lib/gssapi/krb5/Makefile.in +++ b/src/lib/gssapi/krb5/Makefile.in @@ -1,4 +1,4 @@ -CFLAGS = $(CCOPTS) $(DEFS) -I. -I$(srcdir) -I../generic -I$(srcdir)/../generic -I$(srcdir)/../../crypto/md5 -DUSE_AUTOCONF_H +CFLAGS = $(CCOPTS) $(DEFS) -I. -I$(srcdir) -I../generic -I$(srcdir)/../generic -DUSE_AUTOCONF_H ##DOSBUILDTOP = ..\..\.. ##DOSLIBNAME=..\gssapi.$(LIBEXT) @@ -50,12 +50,10 @@ SRCS = \ $(srcdir)/inq_names.c \ $(srcdir)/k5seal.c \ $(srcdir)/k5unseal.c \ - $(srcdir)/k5mech.c \ - $(srcdir)/pname_to_uid.c \ + $(srcdir)/krb5_gss_glue.c \ $(srcdir)/process_context_token.c \ $(srcdir)/rel_cred.c \ $(srcdir)/rel_name.c \ - $(srcdir)/rel_oid.c \ $(srcdir)/seal.c \ $(srcdir)/ser_sctx.c \ $(srcdir)/sign.c \ @@ -65,8 +63,13 @@ SRCS = \ $(srcdir)/util_seed.c \ $(srcdir)/util_seqnum.c \ $(srcdir)/verify.c \ + $(srcdir)/wrap_size_limit.c \ gssapi_err_krb5.c +# $(srcdir)/pname_to_uid.c \ +# $(srcdir)/k5mech.c \ +# $(srcdir)/rel_oid.c + OBJS = \ accept_sec_context.$(OBJEXT) \ acquire_cred.$(OBJEXT) \ @@ -87,12 +90,10 @@ OBJS = \ inq_names.$(OBJEXT) \ k5seal.$(OBJEXT) \ k5unseal.$(OBJEXT) \ - k5mech.$(OBJEXT) \ - pname_to_uid.$(OBJEXT) \ + krb5_gss_glue.$(OBJEXT) \ process_context_token.$(OBJEXT) \ rel_cred.$(OBJEXT) \ rel_name.$(OBJEXT) \ - rel_oid.$(OBJEXT) \ seal.$(OBJEXT) \ ser_sctx.$(OBJEXT) \ sign.$(OBJEXT) \ @@ -102,15 +103,23 @@ OBJS = \ util_seed.$(OBJEXT) \ util_seqnum.$(OBJEXT) \ verify.$(OBJEXT) \ + wrap_size_limit.$(OBJEXT) \ gssapi_err_krb5.$(OBJEXT) +# k5mech.$(OBJEXT) \ +# pname_to_uid.$(OBJEXT) \ +# rel_oid.$(OBJEXT) + HDRS= $(ETHDRS) EHDRDIR=$(TOP)/include/gssapi +EXPORTED_HEADERS= gssapi_krb5.h all-unix:: $(SRCS) $(HDRS) includes $(OBJS) all-mac:: $(SRCS) $(HDRS) includes $(OBJS) all-windows:: $(SRCS) $(HDRS) includes $(OBJS) + if not exist $(EHDRDIR)\nul mkdir $(EHDRDIR) + copy gssapi_krb5.h $(EHDRDIR) clean-unix:: $(RM) $(ETHDRS) $(ETSRCS) shared/* @@ -119,6 +128,14 @@ clean-mac:: $(RM) $(ETHDRS) $(ETSRCS) shared/* clean-windows:: + $(RM) $(EHDRDIR)\gssapi_krb5.h + if exist $(EHDRDIR)\nul rmdir $(EHDRDIR) + +install:: + @set -x; for f in $(EXPORTED_HEADERS) ; \ + do $(INSTALL_DATA) $(srcdir)/$$f \ + $(DESTDIR)$(KRB5_INCDIR)/gssapi/$$f ; \ + done depend:: $(ETSRCS) diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index 79d41b71d..09ed41247 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -21,9 +21,40 @@ */ #include "gssapiP_krb5.h" -#include "rsa-md5.h" #include +/* + * $Id$ + */ + +#if 0 + +/* XXXX This widen/narrow stuff is bletcherous, but it seems to be + necessary. Perhaps there is a "better" way, but I don't know what it + is */ + +#include +static krb5_error_code +rd_req_keyproc(krb5_pointer keyprocarg, krb5_principal server, + krb5_kvno kvno, krb5_keyblock **keyblock) +#include +{ + krb5_error_code code; + krb5_keytab_entry ktentry; + + if (code = krb5_kt_get_entry((krb5_keytab) keyprocarg, server, kvno, + &ktentry)) + return(code); + + code = krb5_copy_keyblock(&ktentry.key, keyblock); + + (void) krb5_kt_free_entry(&ktentry); + + return(code); +} + +#endif + /* Decode, decrypt and store the forwarded creds in the local ccache. */ static krb5_error_code rd_and_store_for_creds(context, auth_context, inbuf) @@ -56,12 +87,11 @@ cleanup: } OM_uint32 -krb5_gss_accept_sec_context(ct, minor_status, context_handle, +krb5_gss_accept_sec_context(minor_status, context_handle, verifier_cred_handle, input_token, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle) - void *ct; OM_uint32 *minor_status; gss_ctx_id_t *context_handle; gss_cred_id_t verifier_cred_handle; @@ -74,7 +104,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, OM_uint32 *time_rec; gss_cred_id_t *delegated_cred_handle; { - krb5_context context = ct; + krb5_context context; unsigned char *ptr, *ptr2; char *sptr; long tmp; @@ -89,14 +119,20 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, krb5_principal name; int gss_flags; krb5_gss_ctx_id_rec *ctx; + krb5_enctype enctype; krb5_timestamp now; gss_buffer_desc token; + int err; krb5_auth_context auth_context = NULL; krb5_ticket * ticket = NULL; int option_id; krb5_data option; krb5_auth_context auth_context_cred = NULL; + const gss_OID_desc *mech_used = NULL; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); /* set up returns to be freeable */ @@ -141,14 +177,43 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, return(GSS_S_NO_CRED); } - /* verify the token's integrity, and leave the token in ap_req */ + /* verify the token's integrity, and leave the token in ap_req. + figure out which mech oid was used, and save it */ ptr = (unsigned char *) input_token->value; - if (! g_verify_token_header((gss_OID) gss_mech_krb5, &(ap_req.length), - &ptr, KG_TOK_CTX_AP_REQ, input_token->length)) { - *minor_status = 0; - return(GSS_S_DEFECTIVE_TOKEN); + if (err = g_verify_token_header((gss_OID) gss_mech_krb5, &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length)) { + /* + * Previous versions of this library used the old mech_id + * and some broken behavior (wrong IV on checksum + * encryption). We support the old mech_id for + * compatibility, and use it to decide when to use the + * old behavior. + */ + if (err != G_WRONG_MECH || + (err = g_verify_token_header((gss_OID) gss_mech_krb5_old, + &(ap_req.length), + &ptr, KG_TOK_CTX_AP_REQ, + input_token->length))) { + *minor_status = err; + return(GSS_S_DEFECTIVE_TOKEN); + } else { + if (! cred->prerfc_mech) { + *minor_status = G_WRONG_MECH; + return(GSS_S_DEFECTIVE_TOKEN); + } + + mech_used = gss_mech_krb5_old; + } + } else { + if (! cred->rfc_mech) { + *minor_status = G_WRONG_MECH; + return(GSS_S_DEFECTIVE_TOKEN); + } + + mech_used = gss_mech_krb5; } sptr = (char *) ptr; @@ -180,6 +245,17 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, krb5_auth_con_getauthenticator(context, auth_context, &authdat); +#if 0 + /* make sure the necessary parts of the authdat are present */ + + if ((authdat->authenticator->subkey == NULL) || + (authdat->ticket->enc_part2 == NULL)) { + krb5_free_tkt_authent(authdat); + *minor_status = KG_NO_SUBKEY; + return(GSS_S_FAILURE); + } +#endif + /* verify that the checksum is correct */ /* @@ -210,13 +286,13 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, TREAD_INT(ptr, tmp, bigend); - if (tmp != RSA_MD5_CKSUM_LENGTH) { + if (tmp != krb5_checksum_size(context, CKSUMTYPE_RSA_MD5)) { ptr = (unsigned char *) authdat->checksum->contents; bigend = 1; TREAD_INT(ptr, tmp, bigend); - if (tmp != RSA_MD5_CKSUM_LENGTH) { + if (tmp != krb5_checksum_size(context, CKSUMTYPE_RSA_MD5)) { xfree(md5.contents); krb5_free_authenticator(context, authdat); *minor_status = KG_BAD_LENGTH; @@ -226,7 +302,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, /* at this point, bigend is set according to the initiator's byte order */ - if ((code = kg_checksum_channel_bindings(input_chan_bindings, &md5, + if ((code = kg_checksum_channel_bindings(context, input_chan_bindings, &md5, bigend))) { krb5_free_authenticator(context, authdat); *minor_status = code; @@ -289,21 +365,13 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, break; - default : + /* default: */ + /* unknown options aren't an error */ - /* any other options are unrecognized. return - generic GSS_C_FAILURE error with a minor status - of KRB5_PARSE_MALFORMED (XXX this is probably - not the right error, since it is used for - string parsing errors not token parsing errors.) */ - - *minor_status = KRB5_PARSE_MALFORMED; - return(GSS_S_FAILURE); } /* switch */ } /* while */ } /* if */ - /* create the ctx struct and start filling it in */ if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec))) @@ -313,6 +381,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, } memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->mech_used = mech_used; ctx->auth_context = auth_context; ctx->initiate = 0; ctx->gss_flags = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | @@ -345,20 +414,42 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, return(GSS_S_FAILURE); } + switch(ctx->subkey->enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_CRC: + enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = 0; + ctx->cksum_size = 8; + ctx->sealalg = 0; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = 3; + ctx->cksum_size = 16; + ctx->sealalg = 1; + break; +#endif + default: + return GSS_S_FAILURE; + } + /* fill in the encryption descriptors */ - krb5_use_enctype(context, &ctx->enc.eblock, ENCTYPE_DES_CBC_RAW); + krb5_use_enctype(context, &ctx->enc.eblock, enctype); ctx->enc.processed = 0; - if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key))) + + if (code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key)) return(code); for (i=0; ienc.key->length; i++) /*SUPPRESS 113*/ ctx->enc.key->contents[i] ^= 0xf0; - krb5_use_enctype(context, &ctx->seq.eblock, ENCTYPE_DES_CBC_RAW); + krb5_use_enctype(context, &ctx->seq.eblock, enctype); ctx->seq.processed = 0; if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq.key))) return(code); + ctx->endtime = ticket->enc_part2->times.endtime; ctx->flags = ticket->enc_part2->flags; @@ -366,6 +457,10 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, krb5_auth_con_getremoteseqnumber(context, auth_context, &ctx->seq_recv); + g_order_init(&(ctx->seqstate), ctx->seq_recv, + gss_flags & GSS_C_REPLAY_FLAG, + gss_flags & GSS_C_SEQUENCE_FLAG); + /* at this point, the entire context structure is filled in, so it can be released. */ @@ -375,23 +470,23 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, krb5_data ap_rep; unsigned char * ptr; if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); *minor_status = code; return(GSS_S_FAILURE); } krb5_auth_con_getlocalseqnumber(context, auth_context, &ctx->seq_send); - token.length = g_token_size((gss_OID) gss_mech_krb5, ap_rep.length); + token.length = g_token_size((gss_OID) mech_used, ap_rep.length); if ((token.value = (unsigned char *) xmalloc(token.length)) == NULL) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); - *minor_status = code; + *minor_status = ENOMEM; return(GSS_S_FAILURE); } ptr = token.value; - g_make_token_header((gss_OID) gss_mech_krb5, ap_rep.length, - &ptr, KG_TOK_CTX_AP_REP); + g_make_token_header((gss_OID) mech_used, ap_rep.length, + &ptr, KG_TOK_CTX_AP_REP); TWRITE_STR(ptr, ap_rep.data, ap_rep.length); xfree(ap_rep.data); @@ -410,7 +505,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, if ((code = krb5_copy_principal(context, ctx->there, &name))) { if (token.value) xfree(token.value); - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); *minor_status = code; return(GSS_S_FAILURE); @@ -418,14 +513,14 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, } if (mech_type) - *mech_type = (gss_OID) gss_mech_krb5; + *mech_type = (gss_OID) mech_used; if (time_rec) { if ((code = krb5_timeofday(context, &now))) { if (src_name) krb5_free_principal(context, name); xfree(token.value); - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); *minor_status = code; return(GSS_S_FAILURE); @@ -434,7 +529,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, } if (ret_flags) - *ret_flags = ctx->gss_flags; + *ret_flags = KG_IMPLFLAGS(gss_flags); ctx->established = 1; @@ -445,7 +540,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, krb5_free_principal(context, name); if (token.value) xfree(token.value); - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); *minor_status = (OM_uint32) G_VALIDATE_FAILED; return(GSS_S_FAILURE); @@ -460,7 +555,7 @@ krb5_gss_accept_sec_context(ct, minor_status, context_handle, } if (token.value) xfree(token.value); - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t *) &ctx, NULL); *minor_status = (OM_uint32) G_VALIDATE_FAILED; return(GSS_S_FAILURE); diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 526e6dddb..b2d6ce111 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -27,6 +27,10 @@ #include #endif +/* + * $Id$ + */ + /* get credentials corresponding to a key in the krb5 keytab. If the default name is requested, return the name in output_princ. If output_princ is non-NULL, the caller will use or free it, regardless @@ -35,14 +39,13 @@ */ static OM_uint32 -acquire_accept_cred(ctx, minor_status, desired_name, output_princ, cred) - void *ctx; +acquire_accept_cred(context, minor_status, desired_name, output_princ, cred) + krb5_context context; OM_uint32 *minor_status; gss_name_t desired_name; krb5_principal *output_princ; krb5_gss_cred_id_rec *cred; { - krb5_context context = ctx; krb5_error_code code; krb5_principal princ; krb5_keytab kt; @@ -65,6 +68,7 @@ acquire_accept_cred(ctx, minor_status, desired_name, output_princ, cred) if (desired_name == (gss_name_t) NULL) { if (code = krb5_sname_to_principal(context, NULL, NULL, KRB5_NT_SRV_HST, &princ)) { + (void) krb5_kt_close(context, kt); *minor_status = code; return(GSS_S_FAILURE); } @@ -76,6 +80,7 @@ acquire_accept_cred(ctx, minor_status, desired_name, output_princ, cred) /* iterate over the keytab searching for the principal */ if (code = krb5_kt_start_seq_get(context, kt, &cur)) { + (void) krb5_kt_close(context, kt); *minor_status = code; return(GSS_S_FAILURE); } @@ -92,16 +97,19 @@ acquire_accept_cred(ctx, minor_status, desired_name, output_princ, cred) if (code == KRB5_KT_END) { /* this means that the principal wasn't in the keytab */ (void)krb5_kt_end_seq_get(context, kt, &cur); + (void) krb5_kt_close(context, kt); *minor_status = KG_KEYTAB_NOMATCH; return(GSS_S_CRED_UNAVAIL); } else if (code) { /* this means some error occurred reading the keytab */ (void)krb5_kt_end_seq_get(context, kt, &cur); + (void) krb5_kt_close(context, kt); *minor_status = code; return(GSS_S_FAILURE); } else { /* this means that we found a matching entry */ if (code = krb5_kt_end_seq_get(context, kt, &cur)) { + (void) krb5_kt_close(context, kt); *minor_status = code; return(GSS_S_FAILURE); } @@ -204,7 +212,6 @@ acquire_init_cred(context, minor_status, desired_name, output_princ, cred) if (got_endtime == 0) { cred->tgt_expire = creds.times.endtime; got_endtime = 1; - *minor_status = KG_TGT_MISSING; } krb5_free_cred_contents(context, &creds); } @@ -215,6 +222,12 @@ acquire_init_cred(context, minor_status, desired_name, output_princ, cred) (void)krb5_cc_close(context, ccache); *minor_status = code; return(GSS_S_FAILURE); + } else if (! got_endtime) { + /* this means the ccache was entirely empty */ + (void)krb5_cc_end_seq_get(context, ccache, &cur); + (void)krb5_cc_close(context, ccache); + *minor_status = KG_EMPTY_CCACHE; + return(GSS_S_FAILURE); } else { /* this means that we found an endtime to use. */ if (code = krb5_cc_end_seq_get(context, ccache, &cur)) { @@ -239,10 +252,9 @@ acquire_init_cred(context, minor_status, desired_name, output_princ, cred) /*ARGSUSED*/ OM_uint32 -krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, +krb5_gss_acquire_cred(minor_status, desired_name, time_req, desired_mechs, cred_usage, output_cred_handle, actual_mechs, time_rec) - void *ctx; OM_uint32 *minor_status; gss_name_t desired_name; OM_uint32 time_req; @@ -252,13 +264,17 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, gss_OID_set *actual_mechs; OM_uint32 *time_rec; { - krb5_context context = ctx; + krb5_context context; size_t i; krb5_gss_cred_id_t cred; - gss_OID_set mechs; + gss_OID_set valid_mechs, ret_mechs; + int req_old, req_new; OM_uint32 ret; krb5_error_code code; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* make sure all outputs are valid */ *output_cred_handle = NULL; @@ -279,11 +295,26 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, /* verify that the requested mechanism set is the default, or contains krb5 */ - if (desired_mechs != GSS_C_NULL_OID_SET) { - for (i=0; icount; i++) + if (desired_mechs == GSS_C_NULL_OID_SET) { + valid_mechs = gss_mech_set_krb5_both; + } else { + req_old = 0; + req_new = 0; + + for (i=0; icount; i++) { + if (g_OID_equal(gss_mech_krb5_old, &(desired_mechs->elements[i]))) + req_old++; if (g_OID_equal(gss_mech_krb5, &(desired_mechs->elements[i]))) - break; - if (i == desired_mechs->count) { + req_new++; + } + + if (req_old && req_new) { + valid_mechs = gss_mech_set_krb5_both; + } else if (req_old) { + valid_mechs = gss_mech_set_krb5_old; + } else if (req_new) { + valid_mechs = gss_mech_set_krb5; + } else { *minor_status = 0; return(GSS_S_BAD_MECH); } @@ -300,6 +331,9 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, cred->usage = cred_usage; cred->princ = NULL; + cred->actual_mechs = valid_mechs; + cred->prerfc_mech = req_old; + cred->rfc_mech = req_new; cred->keytab = NULL; cred->ccache = NULL; @@ -390,7 +424,7 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, /* create mechs */ if (actual_mechs) { - if (! g_copy_OID_set(gss_mech_set_krb5, &mechs)) { + if (! g_copy_OID_set(cred->actual_mechs, &ret_mechs)) { if (cred->ccache) (void)krb5_cc_close(context, cred->ccache); if (cred->keytab) @@ -406,8 +440,8 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, /* intern the credential handle */ if (! kg_save_cred_id((gss_cred_id_t) cred)) { - free(mechs->elements); - free(mechs); + free(ret_mechs->elements); + free(ret_mechs); if (cred->ccache) (void)krb5_cc_close(context, cred->ccache); if (cred->keytab) @@ -424,19 +458,18 @@ krb5_gss_acquire_cred(ctx, minor_status, desired_name, time_req, *minor_status = 0; *output_cred_handle = (gss_cred_id_t) cred; if (actual_mechs) - *actual_mechs = mechs; + *actual_mechs = ret_mechs; return(GSS_S_COMPLETE); } /* V2 interface */ OM_uint32 -krb5_gss_add_cred(ctx, minor_status, input_cred_handle, +krb5_gss_add_cred(minor_status, input_cred_handle, desired_name, desired_mech, cred_usage, initiator_time_req, acceptor_time_req, output_cred_handle, actual_mechs, initiator_time_rec, acceptor_time_rec) - void *ctx; OM_uint32 *minor_status; gss_cred_id_t input_cred_handle; gss_name_t desired_name; @@ -449,12 +482,19 @@ krb5_gss_add_cred(ctx, minor_status, input_cred_handle, OM_uint32 *initiator_time_rec; OM_uint32 *acceptor_time_rec; { - krb5_context context = ctx; /* - * This does not apply to our single-mechanism implementation. Until we - * come up with a better error code, return failure. + * This does not apply to our single-mechanism implementation. Decide + * if the correct error is BAD_MECH or DUPLICATE_ELEMENT. */ + + /* verify that the requested mechanism is the default, or + is krb5 */ + + if ((desired_mech != GSS_C_NULL_OID) && + (g_OID_equal(desired_mech, gss_mech_krb5))) + return(GSS_S_BAD_MECH); + *minor_status = 0; - return(GSS_S_FAILURE); + return(GSS_S_DUPLICATE_ELEMENT); } diff --git a/src/lib/gssapi/krb5/compare_name.c b/src/lib/gssapi/krb5/compare_name.c index 19b94f452..75a534220 100644 --- a/src/lib/gssapi/krb5/compare_name.c +++ b/src/lib/gssapi/krb5/compare_name.c @@ -20,17 +20,24 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_compare_name(ctx, minor_status, name1, name2, name_equal) - void *ctx; +krb5_gss_compare_name(minor_status, name1, name2, name_equal) OM_uint32 *minor_status; gss_name_t name1; gss_name_t name2; int *name_equal; { - krb5_context context = ctx; + krb5_context context; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (! kg_validate_name(name1)) { *minor_status = (OM_uint32) G_VALIDATE_FAILED; return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); diff --git a/src/lib/gssapi/krb5/configure.in b/src/lib/gssapi/krb5/configure.in index d5fc0695f..a05531893 100644 --- a/src/lib/gssapi/krb5/configure.in +++ b/src/lib/gssapi/krb5/configure.in @@ -4,4 +4,5 @@ AC_PROG_AWK AC_CHECK_HEADERS(stdlib.h) V5_SHARED_LIB_OBJS SubdirLibraryRule([${OBJS}]) +CopySrcHeader(gssapi_krb5.h,[$](BUILDTOP)/include/gssapi) V5_AC_OUTPUT_MAKEFILE diff --git a/src/lib/gssapi/krb5/context_time.c b/src/lib/gssapi/krb5/context_time.c index 3bc42e603..76f1489b7 100644 --- a/src/lib/gssapi/krb5/context_time.c +++ b/src/lib/gssapi/krb5/context_time.c @@ -22,19 +22,25 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_context_time(ct, minor_status, context_handle, time_rec) - void *ct; +krb5_gss_context_time(minor_status, context_handle, time_rec) OM_uint32 *minor_status; gss_ctx_id_t context_handle; OM_uint32 *time_rec; { - krb5_context context = ct; + krb5_context context; krb5_error_code code; krb5_gss_ctx_id_rec *ctx; krb5_timestamp now; krb5_deltat lifetime; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* validate the context handle */ if (! kg_validate_ctx_id(context_handle)) { *minor_status = (OM_uint32) G_VALIDATE_FAILED; diff --git a/src/lib/gssapi/krb5/delete_sec_context.c b/src/lib/gssapi/krb5/delete_sec_context.c index fded9afa8..5b5ff74fa 100644 --- a/src/lib/gssapi/krb5/delete_sec_context.c +++ b/src/lib/gssapi/krb5/delete_sec_context.c @@ -22,16 +22,22 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_delete_sec_context(ct, minor_status, context_handle, output_token) - void *ct; +krb5_gss_delete_sec_context(minor_status, context_handle, output_token) OM_uint32 *minor_status; gss_ctx_id_t *context_handle; gss_buffer_t output_token; { - krb5_context context = ct; + krb5_context context; krb5_gss_ctx_id_rec *ctx; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (output_token) { output_token->length = 0; output_token->value = NULL; @@ -71,6 +77,9 @@ krb5_gss_delete_sec_context(ct, minor_status, context_handle, output_token) ctx = (gss_ctx_id_t) *context_handle; + if (ctx->seqstate) + g_order_free(&(ctx->seqstate)); + if (ctx->enc.processed) krb5_finish_key(context, &ctx->enc.eblock); krb5_free_keyblock(context, ctx->enc.key); @@ -86,6 +95,8 @@ krb5_gss_delete_sec_context(ct, minor_status, context_handle, output_token) if (ctx->auth_context) krb5_auth_con_free(context, ctx->auth_context); + /* Zero out context */ + memset(ctx, 0, sizeof(*ctx)); xfree(ctx); /* zero the handle itself */ diff --git a/src/lib/gssapi/krb5/disp_name.c b/src/lib/gssapi/krb5/disp_name.c index a9cbcae06..77f520035 100644 --- a/src/lib/gssapi/krb5/disp_name.c +++ b/src/lib/gssapi/krb5/disp_name.c @@ -23,18 +23,20 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_display_name(ctx, minor_status, input_name, output_name_buffer, +krb5_gss_display_name(minor_status, input_name, output_name_buffer, output_name_type) - void *ctx; OM_uint32 *minor_status; gss_name_t input_name; gss_buffer_t output_name_buffer; gss_OID *output_name_type; { - krb5_context context = ctx; + krb5_context context; krb5_error_code code; char *str; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + output_name_buffer->length = 0; output_name_buffer->value = NULL; diff --git a/src/lib/gssapi/krb5/disp_status.c b/src/lib/gssapi/krb5/disp_status.c index 326d3fc7c..143f7a624 100644 --- a/src/lib/gssapi/krb5/disp_status.c +++ b/src/lib/gssapi/krb5/disp_status.c @@ -32,9 +32,8 @@ static int init_et = 0; /**/ OM_uint32 -krb5_gss_display_status(ctx, minor_status, status_value, status_type, +krb5_gss_display_status(minor_status, status_value, status_type, mech_type, message_context, status_string) - void *ctx; OM_uint32 *minor_status; OM_uint32 status_value; int status_type; @@ -42,10 +41,13 @@ krb5_gss_display_status(ctx, minor_status, status_value, status_type, OM_uint32 *message_context; gss_buffer_t status_string; { - krb5_context context = ctx; + krb5_context context; status_string->length = 0; status_string->value = NULL; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if ((mech_type != GSS_C_NULL_OID) && (! g_OID_equal(gss_mech_krb5, mech_type))) { *minor_status = 0; diff --git a/src/lib/gssapi/krb5/export_sec_context.c b/src/lib/gssapi/krb5/export_sec_context.c index 180cc2ef5..fba8a684b 100644 --- a/src/lib/gssapi/krb5/export_sec_context.c +++ b/src/lib/gssapi/krb5/export_sec_context.c @@ -28,20 +28,21 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_export_sec_context(ct, - minor_status, context_handle, interprocess_token) - void *ct; +krb5_gss_export_sec_context(minor_status, context_handle, interprocess_token) OM_uint32 *minor_status; gss_ctx_id_t *context_handle; gss_buffer_t interprocess_token; { - krb5_context ser_ctx = ct; + krb5_context context; krb5_error_code kret; OM_uint32 retval; size_t bufsize, blen; - krb5_gss_ctx_id_t *ctx; + krb5_gss_ctx_id_t ctx; krb5_octet *obuffer, *obp; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* Assume a tragic failure */ obuffer = (krb5_octet *) NULL; retval = GSS_S_FAILURE; @@ -53,12 +54,12 @@ krb5_gss_export_sec_context(ct, goto error_out; } - ctx = (krb5_gss_ctx_id_t *) *context_handle; + ctx = (krb5_gss_ctx_id_t) *context_handle; /* Determine size needed for externalization of context */ bufsize = 0; - if ((kret = krb5_size_opaque(ser_ctx, KG_CONTEXT, (krb5_pointer) ctx, - &bufsize))) + if ((kret = kg_ctx_size(context, (krb5_pointer) ctx, + &bufsize))) goto error_out; /* Allocate the buffer */ @@ -70,8 +71,8 @@ krb5_gss_export_sec_context(ct, obp = obuffer; blen = bufsize; /* Externalize the context */ - if ((kret = krb5_externalize_opaque(ser_ctx, KG_CONTEXT, - (krb5_pointer)ctx, &obp, &blen))) + if ((kret = kg_ctx_externalize(context, + (krb5_pointer) ctx, &obp, &blen))) goto error_out; /* Success! Return the buffer */ @@ -81,23 +82,7 @@ krb5_gss_export_sec_context(ct, retval = GSS_S_COMPLETE; /* Now, clean up the context state */ - (void) kg_delete_ctx_id((gss_ctx_id_t) ctx); - if (ctx->enc.processed) - krb5_finish_key(ser_ctx, &ctx->enc.eblock); - krb5_free_keyblock(ser_ctx, ctx->enc.key); - if (ctx->seq.processed) - krb5_finish_key(ser_ctx, &ctx->seq.eblock); - krb5_free_keyblock(ser_ctx, ctx->seq.key); - krb5_free_principal(ser_ctx, ctx->here); - krb5_free_principal(ser_ctx, ctx->there); - krb5_free_keyblock(ser_ctx, ctx->subkey); - - if (ctx->auth_context) - krb5_auth_con_free(ser_ctx, ctx->auth_context); - - /* Zero out context */ - memset(ctx, 0, sizeof(*ctx)); - xfree(ctx); + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); *context_handle = GSS_C_NO_CONTEXT; return (GSS_S_COMPLETE); diff --git a/src/lib/gssapi/krb5/get_tkt_flags.c b/src/lib/gssapi/krb5/get_tkt_flags.c index 2e73cacfe..5dd91064f 100644 --- a/src/lib/gssapi/krb5/get_tkt_flags.c +++ b/src/lib/gssapi/krb5/get_tkt_flags.c @@ -22,6 +22,10 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 gss_krb5_get_tkt_flags(minor_status, context_handle, ticket_flags) OM_uint32 *minor_status; diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 53c4f4691..7ccc4b828 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -23,7 +23,11 @@ #ifndef _GSSAPIP_KRB5_H_ #define _GSSAPIP_KRB5_H_ -#include "k5-int.h" +/* + * $Id$ + */ + +#include #include /* work around sunos braindamage */ @@ -34,12 +38,15 @@ #undef minor #endif -/* this must be after "krb5.h", since krb5 #defines xfree(), too */ #ifndef _MACINTOSH #include "../generic/gssapiP_generic.h" #else #include "gssapiP_generic.h" #endif + +/* The include of gssapi_krb5.h will dtrt with the above #defines in + * effect. + */ #include "gssapi_krb5.h" #include "gssapi_err_krb5.h" @@ -56,6 +63,10 @@ #define KG_TOK_WRAP_MSG 0x0201 #define KG_TOK_DEL_CTX 0x0102 +#define KG_IMPLFLAGS(x) (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG | \ + ((x) & (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \ + GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG))) + #define KRB5_GSS_FOR_CREDS_OPTION 1 /** internal types **/ @@ -66,6 +77,9 @@ typedef struct _krb5_gss_cred_id_rec { /* name/type of credential */ gss_cred_usage_t usage; krb5_principal princ; /* this is not interned as a gss_name_t */ + const gss_OID_set_desc *actual_mechs; + int prerfc_mech; /* these are a cache of the set above */ + int rfc_mech; /* keytab (accept) data */ krb5_keytab keytab; @@ -89,21 +103,24 @@ typedef struct _krb5_gss_ctx_id_rec { krb5_principal here; krb5_principal there; krb5_keyblock *subkey; + int signalg; + int cksum_size; + int sealalg; krb5_gss_enc_desc enc; krb5_gss_enc_desc seq; krb5_timestamp endtime; krb5_flags flags; krb5_int32 seq_send; krb5_int32 seq_recv; + void *seqstate; int established; int big_endian; krb5_auth_context auth_context; -} krb5_gss_ctx_id_rec, krb5_gss_ctx_id_t; + const gss_OID_desc *mech_used; +} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t; extern void *kg_vdb; -extern krb5_context kg_context; - /* helper macros */ #define kg_save_name(name) g_save_name(&kg_vdb,name) @@ -127,16 +144,23 @@ OM_uint32 kg_get_defcred OM_uint32 kg_release_defcred PROTOTYPE((OM_uint32 *minor_status)); krb5_error_code kg_checksum_channel_bindings - PROTOTYPE((gss_channel_bindings_t cb, + PROTOTYPE((krb5_context context, gss_channel_bindings_t cb, krb5_checksum *cksum, int bigend)); -krb5_error_code kg_make_seq_num PROTOTYPE((krb5_gss_enc_desc *ed, +krb5_error_code kg_make_seq_num PROTOTYPE((krb5_context context, + krb5_gss_enc_desc *ed, int direction, krb5_int32 seqnum, unsigned char *cksum, unsigned char *buf)); -krb5_error_code kg_make_seed PROTOTYPE((krb5_keyblock *key, - unsigned char *seed)); +krb5_error_code kg_get_seq_num PROTOTYPE((krb5_context context, + krb5_gss_enc_desc *ed, + unsigned char *cksum, unsigned char *buf, int *direction, + krb5_int32 *seqnum)); + +krb5_error_code kg_make_seed PROTOTYPE((krb5_context context, + krb5_keyblock *key, + unsigned char *seed)); int kg_confounder_size PROTOTYPE((krb5_gss_enc_desc *ed)); @@ -145,10 +169,12 @@ krb5_error_code kg_make_confounder PROTOTYPE((krb5_gss_enc_desc *ed, int kg_encrypt_size PROTOTYPE((krb5_gss_enc_desc *ed, int n)); -krb5_error_code kg_encrypt PROTOTYPE((krb5_gss_enc_desc *ed, +krb5_error_code kg_encrypt PROTOTYPE((krb5_context context, + krb5_gss_enc_desc *ed, krb5_pointer iv, krb5_pointer in, krb5_pointer out, int length)); -krb5_error_code kg_decrypt PROTOTYPE((krb5_gss_enc_desc *ed, +krb5_error_code kg_decrypt PROTOTYPE((krb5_context context, + krb5_gss_enc_desc *ed, krb5_pointer iv, krb5_pointer in, krb5_pointer out, int length)); OM_uint32 kg_seal PROTOTYPE((krb5_context context, @@ -178,14 +204,27 @@ OM_uint32 kg_seal_size PROTOTYPE((krb5_context context, OM_uint32 output_size, OM_uint32 *input_size)); -krb5_error_code -kg_ser_context_init PROTOTYPE((krb5_context)); +krb5_error_code kg_ctx_size PROTOTYPE((krb5_context kcontext, + krb5_pointer arg, + size_t *sizep)); + +krb5_error_code kg_ctx_externalize PROTOTYPE((krb5_context kcontext, + krb5_pointer arg, + krb5_octet **buffer, + size_t *lenremain)); + +krb5_error_code kg_ctx_internalize PROTOTYPE((krb5_context kcontext, + krb5_pointer *argp, + krb5_octet **buffer, + size_t *lenremain)); +OM_uint32 kg_get_context PROTOTYPE((OM_uint32 *minor_status, + krb5_context *context)); + /** declarations of internal name mechanism functions **/ OM_uint32 krb5_gss_acquire_cred -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_name_t, /* desired_name */ OM_uint32, /* time_req */ gss_OID_set, /* desired_mechs */ @@ -196,14 +235,12 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_release_cred -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_cred_id_t* /* cred_handle */ )); OM_uint32 krb5_gss_init_sec_context -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_cred_id_t, /* claimant_cred_handle */ gss_ctx_id_t*, /* context_handle */ gss_name_t, /* target_name */ @@ -220,8 +257,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_accept_sec_context -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t*, /* context_handle */ gss_cred_id_t, /* verifier_cred_handle */ gss_buffer_t, /* input_token_buffer */ @@ -236,29 +272,25 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_process_context_token -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_buffer_t /* token_buffer */ )); OM_uint32 krb5_gss_delete_sec_context -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t*, /* context_handle */ gss_buffer_t /* output_token */ )); OM_uint32 krb5_gss_context_time -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ OM_uint32* /* time_rec */ )); OM_uint32 krb5_gss_sign -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ int, /* qop_req */ gss_buffer_t, /* message_buffer */ @@ -266,8 +298,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_verify -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_buffer_t, /* message_buffer */ gss_buffer_t, /* token_buffer */ @@ -275,8 +306,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_seal -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ int, /* conf_req_flag */ int, /* qop_req */ @@ -286,8 +316,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_unseal -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_buffer_t, /* input_message_buffer */ gss_buffer_t, /* output_message_buffer */ @@ -296,8 +325,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_display_status -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ OM_uint32, /* status_value */ int, /* status_type */ gss_OID, /* mech_type */ @@ -306,44 +334,38 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_indicate_mechs -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_OID_set* /* mech_set */ )); OM_uint32 krb5_gss_compare_name -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_name_t, /* name1 */ gss_name_t, /* name2 */ int* /* name_equal */ )); OM_uint32 krb5_gss_display_name -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_name_t, /* input_name */ gss_buffer_t, /* output_name_buffer */ gss_OID* /* output_name_type */ )); OM_uint32 krb5_gss_import_name -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_buffer_t, /* input_name_buffer */ gss_OID, /* input_name_type */ gss_name_t* /* output_name */ )); OM_uint32 krb5_gss_release_name -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_name_t* /* input_name */ )); OM_uint32 krb5_gss_inquire_cred -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_cred_id_t, /* cred_handle */ gss_name_t *, /* name */ OM_uint32 *, /* lifetime */ @@ -352,8 +374,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_inquire_context -PROTOTYPE( (void *, - OM_uint32*, /* minor_status */ +PROTOTYPE( (OM_uint32*, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_name_t*, /* initiator_name */ gss_name_t*, /* acceptor_name */ @@ -366,8 +387,7 @@ PROTOTYPE( (void *, /* New V2 entry points */ OM_uint32 krb5_gss_get_mic -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_qop_t, /* qop_req */ gss_buffer_t, /* message_buffer */ @@ -375,8 +395,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_verify_mic -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_buffer_t, /* message_buffer */ gss_buffer_t, /* message_token */ @@ -384,8 +403,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_wrap -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t, /* context_handle */ int, /* conf_req_flag */ gss_qop_t, /* qop_req */ @@ -395,8 +413,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_unwrap -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t, /* context_handle */ gss_buffer_t, /* input_message_buffer */ gss_buffer_t, /* output_message_buffer */ @@ -405,8 +422,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_wrap_size_limit -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t, /* context_handle */ int, /* conf_req_flag */ gss_qop_t, /* qop_req */ @@ -415,24 +431,21 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_import_name_object -PROTOTYPE( (krb5_context, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ void *, /* input_name */ gss_OID, /* input_name_type */ gss_name_t * /* output_name */ )); OM_uint32 krb5_gss_export_name_object -PROTOTYPE( (krb5_context, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_name_t, /* input_name */ gss_OID, /* desired_name_type */ void * * /* output_name */ )); OM_uint32 krb5_gss_add_cred -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_cred_id_t, /* input_cred_handle */ gss_name_t, /* desired_name */ gss_OID, /* desired_mech */ @@ -446,8 +459,7 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_inquire_cred_by_mech -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_cred_id_t, /* cred_handle */ gss_OID, /* mech_type */ gss_name_t *, /* name */ @@ -457,38 +469,33 @@ PROTOTYPE( (void *, )); OM_uint32 krb5_gss_export_sec_context -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_ctx_id_t *, /* context_handle */ gss_buffer_t /* interprocess_token */ )); OM_uint32 krb5_gss_import_sec_context -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_buffer_t, /* interprocess_token */ gss_ctx_id_t * /* context_handle */ )); +#if 0 OM_uint32 krb5_gss_release_oid PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_OID * /* oid */ )); - +#endif OM_uint32 krb5_gss_internal_release_oid -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_OID * /* oid */ )); OM_uint32 krb5_gss_inquire_names_for_mech -PROTOTYPE( (void *, - OM_uint32 *, /* minor_status */ +PROTOTYPE( (OM_uint32 *, /* minor_status */ gss_OID, /* mechanism */ gss_OID_set * /* name_types */ )); -OM_uint32 kg_get_context(); - #endif /* _GSSAPIP_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et index 0f26773b1..54a126518 100644 --- a/src/lib/gssapi/krb5/gssapi_err_krb5.et +++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et @@ -33,4 +33,6 @@ error_code KG_CTX_INCOMPLETE, "Attempt to use incomplete security context" error_code KG_CONTEXT, "Bad magic number for krb5_gss_ctx_id_t" error_code KG_CRED, "Bad magic number for krb5_gss_cred_id_t" error_code KG_ENC_DESC, "Bad magic number for krb5_gss_enc_desc" +error_code KG_BAD_SEQ, "Sequence number in token is corrupt" +error_code KG_EMPTY_CCACHE, "Credential cache is empty" end diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index e18455755..feec0bc71 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -20,50 +20,62 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_krb5.h" -/** exported constants defined in gssapi_krb5.h **/ +/** exported constants defined in gssapi_krb5{,_nx}.h **/ /* these are bogus, but will compile */ /* - * The OID of the krb5 mechanism, assigned by IETF, is: - * 1.3.5.1.5.2 + * The OID of the draft krb5 mechanism, assigned by IETF, is: + * iso(1) org(3) dod(5) internet(1) security(5) + * kerberosv5(2) = 1.3.5.1.5.2 * The OID of the krb5_name type is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) - * krb5(2) krb5_name(1) = 1.2.840.113554.2.1.2.1 + * krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1 * The OID of the krb5_principal type is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) - * krb5(2) krb5_principal(2) = 1.2.840.113554.2.1.2.2 + * krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2 + * The OID of the proposed standard krb5 mechanism is: + * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) + * krb5(2) = 1.2.840.113554.1.2.2 + * */ /* * Encoding rules: The first two values are encoded in one byte as 40 * * value1 + value2. Subsequent values are encoded base 128, most - * significant digit first, with the high bit set on all octets except - * the last in each value's encoding. + * significant digit first, with the high bit (\200) set on all octets + * except the last in each value's encoding. */ static const gss_OID_desc oids[] = { - /* this OID is from Ted. It's not official yet, but it's close. */ + /* this is the unofficial, wrong OID */ {5, "\053\005\001\005\002"}, + /* this is the official, rfc-specified OID */ + {9, "\052\206\110\206\367\022\001\002\002"}, {10, "\052\206\110\206\367\022\001\002\002\001"}, {10, "\052\206\110\206\367\022\001\002\002\002"}, - {9, "\052\206\110\206\367\022\001\002\002"}, }; -const gss_OID_desc * const gss_mech_krb5 = oids+0; +const gss_OID_desc * const gss_mech_krb5_old = oids+0; +const gss_OID_desc * const gss_mech_krb5 = oids+1; const gss_OID_desc * const gss_nt_krb5_name = oids+1; -const gss_OID_desc * const gss_nt_krb5_principal = oids+2; -const gss_OID_desc * const gss_new_mech_krb5 = oids+3; +const gss_OID_desc * const gss_nt_krb5_principal = oids+3; static const gss_OID_set_desc oidsets[] = { - {1, (gss_OID) oids}, + {1, (gss_OID) oids+0}, + {1, (gss_OID) oids+1}, + {2, (gss_OID) oids+0}, }; -const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+0; - -krb5_context kg_context; +const gss_OID_set_desc * const gss_mech_set_krb5_old = oidsets+0; +const gss_OID_set_desc * const gss_mech_set_krb5 = oidsets+1; +const gss_OID_set_desc * const gss_mech_set_krb5_both = oidsets+2; void *kg_vdb = NULL; @@ -83,10 +95,7 @@ kg_get_defcred(minor_status, cred) if (defcred == GSS_C_NO_CREDENTIAL) { OM_uint32 major; - if (!kg_context && kg_get_context()) - return GSS_S_FAILURE; - - if ((major = krb5_gss_acquire_cred(kg_context, minor_status, + if ((major = krb5_gss_acquire_cred(minor_status, (gss_name_t) NULL, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, GSS_C_INITIATE, &defcred, NULL, NULL)) && @@ -110,19 +119,24 @@ kg_release_defcred(minor_status) return(GSS_S_COMPLETE); } - if (!kg_context && kg_get_context()) - return GSS_S_FAILURE; - - return(krb5_gss_release_cred(kg_context, minor_status, &defcred)); + return(krb5_gss_release_cred(minor_status, &defcred)); } OM_uint32 -kg_get_context() +kg_get_context(minor_status, context) + OM_uint32 *minor_status; + krb5_context *context; { - if (kg_context) - return GSS_S_COMPLETE; - if (krb5_init_context(&kg_context)) - return GSS_S_FAILURE; - krb5_init_ets(kg_context); - return GSS_S_COMPLETE; + static krb5_context kg_context = NULL; + krb5_error_code code; + + if ((! kg_context) && + (code = krb5_init_context(&kg_context))) { + *minor_status = (OM_uint32) code; + return GSS_S_FAILURE; + } + + *context = kg_context; + *minor_status = 0; + return GSS_S_COMPLETE; } diff --git a/src/lib/gssapi/krb5/gssapi_krb5.h b/src/lib/gssapi/krb5/gssapi_krb5.h index 450081d97..71182f22b 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.h +++ b/src/lib/gssapi/krb5/gssapi_krb5.h @@ -28,10 +28,13 @@ #else #include #endif -#include "krb5.h" +#include extern const gss_OID_desc * const gss_mech_krb5; +extern const gss_OID_desc * const gss_mech_krb5_old; extern const gss_OID_set_desc * const gss_mech_set_krb5; +extern const gss_OID_set_desc * const gss_mech_set_krb5_old; +extern const gss_OID_set_desc * const gss_mech_set_krb5_both; extern const gss_OID_desc * const gss_nt_krb5_name; extern const gss_OID_desc * const gss_nt_krb5_principal; @@ -49,4 +52,11 @@ OM_uint32 gss_krb5_get_tkt_flags krb5_flags *ticket_flags)); +/* this is for backward compatibility only. It is declared here for + completeness, but should not be used */ + +OM_uint32 krb5_gss_set_backward_mode + PROTOTYPE((OM_uint32 *minor_status, + int mode)); + #endif /* _GSSAPI_KRB5_H_ */ diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c index ee44132ae..5c2c6f43a 100644 --- a/src/lib/gssapi/krb5/import_name.c +++ b/src/lib/gssapi/krb5/import_name.c @@ -20,7 +20,12 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_krb5.h" + #ifndef NO_PASSWORD #include #endif @@ -39,21 +44,24 @@ */ OM_uint32 -krb5_gss_import_name(ctx, minor_status, input_name_buffer, +krb5_gss_import_name(minor_status, input_name_buffer, input_name_type, output_name) - void *ctx; OM_uint32 *minor_status; gss_buffer_t input_name_buffer; gss_OID input_name_type; gss_name_t *output_name; { - krb5_context context = ctx; + krb5_context context; krb5_principal princ; krb5_error_code code; char *stringrep, *tmp; #ifndef NO_PASSWORD struct passwd *pw; #endif + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* set up default returns */ *output_name = NULL; @@ -75,13 +83,10 @@ krb5_gss_import_name(ctx, minor_status, input_name_buffer, tmp[input_name_buffer->length] = 0; service = tmp; - if ((host = strchr(tmp, '@')) == NULL) { - xfree(tmp); - *minor_status = (OM_uint32) G_BAD_SERVICE_NAME; - return(GSS_S_BAD_NAME); + if (host = strchr(tmp, '@')) { + *host = '\0'; + host++; } - *host = '\0'; - host++; code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST, &princ); diff --git a/src/lib/gssapi/krb5/import_sec_context.c b/src/lib/gssapi/krb5/import_sec_context.c index d802ecdd0..c1d1bfa72 100644 --- a/src/lib/gssapi/krb5/import_sec_context.c +++ b/src/lib/gssapi/krb5/import_sec_context.c @@ -28,59 +28,46 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_import_sec_context(ct, - minor_status, interprocess_token, context_handle) - void *ct; +krb5_gss_import_sec_context(minor_status, interprocess_token, context_handle) OM_uint32 *minor_status; gss_buffer_t interprocess_token; gss_ctx_id_t *context_handle; { - krb5_context ser_ctx = ct; + krb5_context context; krb5_error_code kret = 0; OM_uint32 retval; size_t blen; - krb5_gss_ctx_id_t *ctx; + krb5_gss_ctx_id_t ctx; krb5_octet *ibp; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* Assume a tragic failure */ - ctx = (krb5_gss_ctx_id_t *) NULL; + ctx = (krb5_gss_ctx_id_t) NULL; retval = GSS_S_FAILURE; *minor_status = 0; /* Internalize the context */ ibp = (krb5_octet *) interprocess_token->value; blen = (size_t) interprocess_token->length; - if ((kret = krb5_internalize_opaque(ser_ctx, KG_CONTEXT, - (krb5_pointer *) &ctx, - &ibp, &blen))) - goto error_out; - + if ((kret = kg_ctx_internalize(context, + (krb5_pointer *) &ctx, + &ibp, &blen))) { + *minor_status = (OM_uint32) kret; + return(GSS_S_FAILURE); + } - /* Make sure that everything is cool. */ - if (!kg_validate_ctx_id((gss_ctx_id_t) ctx)) - goto error_out; + /* intern the context handle */ + if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) { + (void)krb5_gss_delete_sec_context(minor_status, + (gss_ctx_id_t *) &ctx, NULL); + *minor_status = (OM_uint32) G_VALIDATE_FAILED; + return(GSS_S_FAILURE); + } *context_handle = (gss_ctx_id_t) ctx; + *minor_status = 0; return (GSS_S_COMPLETE); - -error_out: - if (ctx) { - (void) kg_delete_ctx_id((gss_ctx_id_t) ctx); - if (ctx->enc.processed) - krb5_finish_key(ser_ctx, &ctx->enc.eblock); - krb5_free_keyblock(ser_ctx, ctx->enc.key); - if (ctx->seq.processed) - krb5_finish_key(ser_ctx, &ctx->seq.eblock); - krb5_free_principal(ser_ctx, ctx->here); - krb5_free_principal(ser_ctx, ctx->there); - krb5_free_keyblock(ser_ctx, ctx->subkey); - - /* Zero out context */ - memset(ctx, 0, sizeof(*ctx)); - xfree(ctx); - } - if (*minor_status == 0) - *minor_status = (OM_uint32) kret; - return(retval); } diff --git a/src/lib/gssapi/krb5/indicate_mechs.c b/src/lib/gssapi/krb5/indicate_mechs.c index 0f78de219..bc53b4434 100644 --- a/src/lib/gssapi/krb5/indicate_mechs.c +++ b/src/lib/gssapi/krb5/indicate_mechs.c @@ -20,11 +20,14 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_indicate_mechs(ctx, minor_status, mech_set) - void *ctx; +krb5_gss_indicate_mechs(minor_status, mech_set) OM_uint32 *minor_status; gss_OID_set *mech_set; { diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 2b671680a..686384216 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -22,22 +22,25 @@ #include "gssapiP_krb5.h" #include -#include "k5-int.h" + +/* + * $Id$ + */ static krb5_error_code -make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, - do_mutual, flags, token) - void *ctx; +make_ap_req(context, auth_context, cred, server, endtime, chan_bindings, + req_flags, flags, mech_type, token) + krb5_context context; krb5_auth_context * auth_context; krb5_gss_cred_id_t cred; krb5_principal server; krb5_timestamp *endtime; gss_channel_bindings_t chan_bindings; - OM_uint32 do_mutual; + OM_uint32 req_flags; krb5_flags *flags; + gss_OID mech_type; gss_buffer_t token; { - krb5_context context = ctx; krb5_flags mk_req_flags = 0; krb5_error_code code; krb5_data checksum_data; @@ -46,19 +49,19 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, krb5_data ap_req; unsigned char *ptr; krb5_data credmsg; - unsigned char ckbuf[24]; /* see the token formats doc */ unsigned char *t; int tlen; krb5_int32 con_flags; ap_req.data = 0; checksum_data.data = 0; + credmsg.data = 0; /* build the checksum buffer */ /* compute the hash of the channel bindings */ - if ((code = kg_checksum_channel_bindings(chan_bindings, &md5, 0))) + if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5, 0))) return(code); /* get an auth_context structure and fill in checksum type */ @@ -92,25 +95,31 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, return(KRB5KRB_ERR_FIELD_TOOLONG); } - /* now allocate a buffer to hold the checksum data and KRB_CRED msg - and write it */ + checksum_data.length = 28+credmsg.length; + } else { + checksum_data.length = 24; + } + + /* now allocate a buffer to hold the checksum data and + (maybe) KRB_CRED msg */ - if ((ptr = (unsigned char *) xmalloc(credmsg.length+28)) == NULL) { + if ((checksum_data.data = + (char *) xmalloc(checksum_data.length)) == NULL) { + if (credmsg.data) krb5_xfree(credmsg.data); - return(ENOMEM); - } + return(ENOMEM); + } - checksum_data.data = (char *) ptr; - checksum_data.length = credmsg.length+28; + ptr = checksum_data.data; - TWRITE_INT(ptr, md5.length, 0); - TWRITE_STR(ptr, (unsigned char *) md5.contents, md5.length); - TWRITE_INT(ptr, do_mutual?GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG - :GSS_C_DELEG_FLAG, 0); + TWRITE_INT(ptr, md5.length, 0); + TWRITE_STR(ptr, (unsigned char *) md5.contents, md5.length); + TWRITE_INT(ptr, KG_IMPLFLAGS(req_flags), 0); - /* done with this, free it */ - xfree(md5.contents); + /* done with this, free it */ + xfree(md5.contents); + if (credmsg.data) { TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0); TWRITE_INT16(ptr, credmsg.length, 0); TWRITE_STR(ptr, (unsigned char *) credmsg.data, credmsg.length); @@ -118,20 +127,6 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, /* free credmsg data */ krb5_xfree(credmsg.data); - - } else { - - ptr = ckbuf; - - TWRITE_INT(ptr, md5.length, 0); - TWRITE_STR(ptr, (unsigned char *) md5.contents, md5.length); - TWRITE_INT(ptr, do_mutual?GSS_C_MUTUAL_FLAG:0, 0); - - /* done with this, free it */ - xfree(md5.contents); - - checksum_data.data = (char *) ckbuf; - checksum_data.length = sizeof(ckbuf); } /* fill in the necessary fields in creds */ @@ -142,7 +137,7 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, goto cleanup; in_creds.times.endtime = *endtime; - in_creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; + /* * Get the credential..., I don't know in 0 is a good value for the * kdcoptions @@ -155,7 +150,7 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, mk_req_flags = AP_OPTS_USE_SUBKEY; - if (do_mutual) + if (req_flags & GSS_C_MUTUAL_FLAG) mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED; if ((code = krb5_mk_req_extended(context, auth_context, mk_req_flags, @@ -169,7 +164,7 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, /* build up the token */ /* allocate space for the token */ - tlen = g_token_size((gss_OID) gss_mech_krb5, ap_req.length); + tlen = g_token_size((gss_OID) mech_type, ap_req.length); if ((t = (unsigned char *) xmalloc(tlen)) == NULL) { code = ENOMEM; @@ -180,38 +175,38 @@ make_ap_req(ctx, auth_context, cred, server, endtime, chan_bindings, ptr = t; - g_make_token_header((gss_OID) gss_mech_krb5, ap_req.length, + g_make_token_header((gss_OID) mech_type, ap_req.length, &ptr, KG_TOK_CTX_AP_REQ); TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length); /* pass it back */ + token->length = tlen; token->value = (void *) t; code = 0; cleanup: - if (checksum_data.data && checksum_data.data != (char *) ckbuf) - free(checksum_data.data); - krb5_free_cred_contents(context, &in_creds); - if (out_creds) - krb5_free_creds(context, out_creds); - if (ap_req.data) - xfree(ap_req.data); - if (code) - krb5_auth_con_free(context, *auth_context); - - return (code); + if (checksum_data.data) + free(checksum_data.data); + krb5_free_cred_contents(context, &in_creds); + if (out_creds) + krb5_free_creds(context, out_creds); + if (ap_req.data) + xfree(ap_req.data); + if (code) + krb5_auth_con_free(context, *auth_context); + + return (code); } OM_uint32 -krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, - context_handle, target_name, mech_type, - req_flags, time_req, input_chan_bindings, - input_token, actual_mech_type, output_token, - ret_flags, time_rec) - void *ct; +krb5_gss_init_sec_context(minor_status, claimant_cred_handle, + context_handle, target_name, mech_type, + req_flags, time_req, input_chan_bindings, + input_token, actual_mech_type, output_token, + ret_flags, time_rec) OM_uint32 *minor_status; gss_cred_id_t claimant_cred_handle; gss_ctx_id_t *context_handle; @@ -226,28 +221,25 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, OM_uint32 *ret_flags; OM_uint32 *time_rec; { - krb5_context context = ct; - krb5_gss_cred_id_t cred; - krb5_error_code code; - krb5_gss_ctx_id_rec *ctx; - krb5_timestamp now; - gss_buffer_desc token; - int i; + krb5_context context; + krb5_gss_cred_id_t cred; + krb5_error_code code; + krb5_gss_ctx_id_rec *ctx; + krb5_timestamp now; + krb5_enctype enctype; + gss_buffer_desc token; + int i; + int err; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); /* set up return values so they can be "freed" successfully */ output_token->length = 0; output_token->value = NULL; if (actual_mech_type) - *actual_mech_type = (gss_OID) gss_mech_krb5; - - /* verify the mech_type */ - - if ((mech_type != GSS_C_NULL_OID) && - (! g_OID_equal(mech_type, gss_mech_krb5))) { - *minor_status = 0; - return(GSS_S_BAD_MECH); - } + *actual_mech_type = NULL; /* verify the credential, or use the default */ /*SUPPRESS 29*/ @@ -267,6 +259,17 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, cred = (krb5_gss_cred_id_t) claimant_cred_handle; + /* verify the mech_type */ + + if (mech_type == GSS_C_NULL_OID) { + mech_type = cred->rfc_mech?gss_mech_krb5:gss_mech_krb5_old; + } else if ((g_OID_equal(mech_type, gss_mech_krb5) && !cred->rfc_mech) || + (g_OID_equal(mech_type, gss_mech_krb5_old) && + !cred->prerfc_mech)) { + *minor_status = 0; + return(GSS_S_BAD_MECH); + } + /* verify that the target_name is valid and usable */ if (! kg_validate_name(target_name)) { @@ -303,6 +306,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, /* fill in the ctx */ memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); + ctx->mech_used = mech_type; ctx->auth_context = NULL; ctx->initiate = 1; ctx->gss_flags = ((req_flags & (GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG)) | @@ -310,6 +314,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, ctx->flags = req_flags & GSS_C_DELEG_FLAG; ctx->seed_init = 0; ctx->big_endian = 0; /* all initiators do little-endian, as per spec */ + ctx->seqstate = 0; if (time_req == 0 || time_req == GSS_C_INDEFINITE) { ctx->endtime = 0; @@ -338,8 +343,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, if ((code = make_ap_req(context, &(ctx->auth_context), cred, ctx->there, &ctx->endtime, input_chan_bindings, - ctx->gss_flags & GSS_C_MUTUAL_FLAG, &ctx->flags, - &token))) { + req_flags, &ctx->flags, mech_type, &token))) { krb5_free_principal(context, ctx->here); krb5_free_principal(context, ctx->there); xfree(ctx); @@ -354,9 +358,29 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, /* fill in the encryption descriptors */ + switch(ctx->subkey->enctype) { + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_CRC: + enctype = ENCTYPE_DES_CBC_RAW; + ctx->signalg = 0; + ctx->cksum_size = 8; + ctx->sealalg = 0; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + enctype = ENCTYPE_DES3_CBC_RAW; + ctx->signalg = 3; + ctx->cksum_size = 16; + ctx->sealalg = 1; + break; +#endif + default: + return GSS_S_FAILURE; + } + /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */ - krb5_use_enctype(context, &ctx->enc.eblock, ENCTYPE_DES_CBC_RAW); + krb5_use_enctype(context, &ctx->enc.eblock, enctype); ctx->enc.processed = 0; if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key))) return(code); @@ -364,7 +388,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, /*SUPPRESS 113*/ ctx->enc.key->contents[i] ^= 0xf0; - krb5_use_enctype(context, &ctx->seq.eblock, ENCTYPE_DES_CBC_RAW); + krb5_use_enctype(context, &ctx->seq.eblock, enctype); ctx->seq.processed = 0; if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq.key))) return(code); @@ -390,7 +414,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, if (time_rec) { if ((code = krb5_timeofday(context, &now))) { xfree(token.value); - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t) ctx, NULL); *minor_status = code; return(GSS_S_FAILURE); @@ -405,7 +429,10 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, *output_token = token; if (ret_flags) - *ret_flags = ctx->gss_flags; + *ret_flags = KG_IMPLFLAGS(req_flags); + + if (actual_mech_type) + *actual_mech_type = mech_type; /* return successfully */ @@ -415,8 +442,11 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, return(GSS_S_CONTINUE_NEEDED); } else { ctx->seq_recv = ctx->seq_send; + g_order_init(&(ctx->seqstate), ctx->seq_recv, + req_flags & GSS_C_REPLAY_FLAG, + req_flags & GSS_C_SEQUENCE_FLAG); ctx->established = 1; - return(GSS_S_COMPLETE); + /* fall through to GSS_S_COMPLETE */ } } else { unsigned char *ptr; @@ -439,15 +469,16 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, if ((ctx->established) || (((gss_cred_id_t) cred) != claimant_cred_handle) || ((req_flags & GSS_C_MUTUAL_FLAG) == 0)) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); + /* XXX this minor status is wrong if an arg was changed */ *minor_status = KG_CONTEXT_ESTABLISHED; return(GSS_S_FAILURE); } if (! krb5_principal_compare(context, ctx->there, (krb5_principal) target_name)) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); *minor_status = 0; return(GSS_S_BAD_NAME); @@ -456,7 +487,7 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, /* verify the token and leave the AP_REP message in ap_rep */ if (input_token == GSS_C_NO_BUFFER) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); *minor_status = 0; return(GSS_S_DEFECTIVE_TOKEN); @@ -464,10 +495,10 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, ptr = (unsigned char *) input_token->value; - if (! g_verify_token_header((gss_OID) gss_mech_krb5, &(ap_rep.length), - &ptr, KG_TOK_CTX_AP_REP, - input_token->length)) { - *minor_status = 0; + if (err = g_verify_token_header((gss_OID) mech_type, &(ap_rep.length), + &ptr, KG_TOK_CTX_AP_REP, + input_token->length)) { + *minor_status = err; return(GSS_S_DEFECTIVE_TOKEN); } @@ -482,9 +513,9 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, * To be removed in 1999 -- proven */ krb5_auth_con_setuseruserkey(context,ctx->auth_context,ctx->subkey); - if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep, - &ap_rep_data))) { - (void)krb5_gss_delete_sec_context(context, minor_status, + if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep, + &ap_rep_data))) { + (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL); *minor_status = code; return(GSS_S_FAILURE); @@ -493,6 +524,9 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, /* store away the sequence number */ ctx->seq_recv = ap_rep_data->seq_number; + g_order_init(&(ctx->seqstate), ctx->seq_recv, + req_flags & GSS_C_REPLAY_FLAG, + req_flags & GSS_C_SEQUENCE_FLAG); /* free the ap_rep_data */ krb5_free_ap_rep_enc_part(context, ap_rep_data); @@ -504,20 +538,25 @@ krb5_gss_init_sec_context(ct, minor_status, claimant_cred_handle, if (time_rec) { if ((code = krb5_timeofday(context, &now))) { - (void)krb5_gss_delete_sec_context(context, minor_status, + (void)krb5_gss_delete_sec_context(minor_status, (gss_ctx_id_t) ctx, NULL); *minor_status = code; return(GSS_S_FAILURE); - } + *time_rec = ctx->endtime - now; } if (ret_flags) - *ret_flags = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG; + *ret_flags = KG_IMPLFLAGS(req_flags); + + if (actual_mech_type) + *actual_mech_type = mech_type; /* success */ *minor_status = 0; - return(GSS_S_COMPLETE); + /* fall through to GSS_S_COMPLETE */ } + + return(GSS_S_COMPLETE); } diff --git a/src/lib/gssapi/krb5/inq_context.c b/src/lib/gssapi/krb5/inq_context.c index c8499212f..5e5746344 100644 --- a/src/lib/gssapi/krb5/inq_context.c +++ b/src/lib/gssapi/krb5/inq_context.c @@ -23,10 +23,9 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_inquire_context(ct, minor_status, context_handle, initiator_name, +krb5_gss_inquire_context(minor_status, context_handle, initiator_name, acceptor_name, lifetime_rec, mech_type, ret_flags, locally_initiated, open) - void *ct; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_name_t *initiator_name; @@ -37,13 +36,16 @@ krb5_gss_inquire_context(ct, minor_status, context_handle, initiator_name, int *locally_initiated; int *open; { - krb5_context context = ct; + krb5_context context; krb5_error_code code; krb5_gss_ctx_id_rec *ctx; krb5_principal init, accept; krb5_timestamp now; krb5_deltat lifetime; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (initiator_name) *initiator_name = (gss_name_t) NULL; if (acceptor_name) @@ -116,7 +118,7 @@ krb5_gss_inquire_context(ct, minor_status, context_handle, initiator_name, *lifetime_rec = lifetime; if (mech_type) - *mech_type = (gss_OID) gss_mech_krb5; + *mech_type = (gss_OID) ctx->mech_used; if (ret_flags) *ret_flags = ctx->gss_flags; diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c index e3e01bf4e..f9811c347 100644 --- a/src/lib/gssapi/krb5/inq_cred.c +++ b/src/lib/gssapi/krb5/inq_cred.c @@ -23,9 +23,8 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret, +krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, cred_usage, mechanisms) - void *ctx; OM_uint32 *minor_status; gss_cred_id_t cred_handle; gss_name_t *name; @@ -33,7 +32,7 @@ krb5_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret, gss_cred_usage_t *cred_usage; gss_OID_set *mechanisms; { - krb5_context context = ctx; + krb5_context context; krb5_gss_cred_id_t cred; krb5_error_code code; krb5_timestamp now; @@ -41,6 +40,9 @@ krb5_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret, krb5_principal ret_name; gss_OID_set mechs; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (name) *name = NULL; if (mechanisms) *mechanisms = NULL; @@ -82,7 +84,7 @@ krb5_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret, } if (mechanisms) - if (! g_copy_OID_set(gss_mech_set_krb5, &mechs)) { + if (! g_copy_OID_set(cred->actual_mechs, &mechs)) { krb5_free_principal(context, ret_name); *minor_status = ENOMEM; return(GSS_S_FAILURE); @@ -113,10 +115,9 @@ krb5_gss_inquire_cred(ctx, minor_status, cred_handle, name, lifetime_ret, /* V2 interface */ OM_uint32 -krb5_gss_inquire_cred_by_mech(ctx, minor_status, cred_handle, +krb5_gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name, initiator_lifetime, acceptor_lifetime, cred_usage) - void *ctx; OM_uint32 *minor_status; gss_cred_id_t cred_handle; gss_OID mech_type; @@ -125,11 +126,14 @@ krb5_gss_inquire_cred_by_mech(ctx, minor_status, cred_handle, OM_uint32 *acceptor_lifetime; gss_cred_usage_t *cred_usage; { - krb5_context context = ctx; + krb5_context context; krb5_gss_cred_id_t cred; OM_uint32 lifetime; OM_uint32 mstat; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* * We only know how to handle our own creds. */ @@ -140,8 +144,7 @@ krb5_gss_inquire_cred_by_mech(ctx, minor_status, cred_handle, } cred = (krb5_gss_cred_id_t) cred_handle; - mstat = krb5_gss_inquire_cred(context, - minor_status, + mstat = krb5_gss_inquire_cred(minor_status, cred_handle, name, &lifetime, diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c index fe61eb01a..948346dc6 100644 --- a/src/lib/gssapi/krb5/inq_names.c +++ b/src/lib/gssapi/krb5/inq_names.c @@ -28,19 +28,23 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_inquire_names_for_mech(context, minor_status, mechanism, name_types) - void *context; +krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types) OM_uint32 *minor_status; gss_OID mechanism; gss_OID_set *name_types; { + krb5_context context; OM_uint32 major, minor; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* * We only know how to handle our own mechanism. */ if ((mechanism != GSS_C_NULL_OID) && - !g_OID_equal(gss_mech_krb5, mechanism)) { + !g_OID_equal(gss_mech_krb5, mechanism) && + !g_OID_equal(gss_mech_krb5_old, mechanism)) { *minor_status = 0; return(GSS_S_FAILURE); } diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c index 4e5c78bd1..254d51680 100644 --- a/src/lib/gssapi/krb5/k5seal.c +++ b/src/lib/gssapi/krb5/k5seal.c @@ -21,11 +21,11 @@ */ #include "gssapiP_krb5.h" -#include "rsa-md5.h" static krb5_error_code make_seal_token(context, enc_ed, seq_ed, seqnum, direction, text, token, - encrypt, toktype, bigend) + signalg, cksum_size, sealalg, encrypt, toktype, + bigend, oid) krb5_context context; krb5_gss_enc_desc *enc_ed; krb5_gss_enc_desc *seq_ed; @@ -33,29 +33,36 @@ make_seal_token(context, enc_ed, seq_ed, seqnum, direction, text, token, int direction; gss_buffer_t text; gss_buffer_t token; + int signalg; + int cksum_size; + int sealalg; int encrypt; int toktype; int bigend; + gss_OID oid; { krb5_error_code code; - krb5_MD5_CTX md5; - krb5_checksum desmac; - krb5_octet cbc_checksum[KRB5_MIT_DES_KEYSIZE]; - int tmsglen, tlen; + char *data_ptr; + krb5_checksum md5cksum; + krb5_checksum cksum; + int conflen, tmsglen, tlen; unsigned char *t, *ptr; /* create the token buffer */ - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) { - if (bigend && !encrypt) + if (toktype == KG_TOK_SEAL_MSG) { + if (bigend && !encrypt) { tmsglen = text->length; - else - tmsglen = (kg_confounder_size(enc_ed)+text->length+8)&(~7); + } else { + conflen = kg_confounder_size(enc_ed); + /* XXX knows that des block size is 8 */ + tmsglen = (conflen+text->length+8)&(~7); + } } else { tmsglen = 0; } - tlen = g_token_size((gss_OID) gss_mech_krb5, 22+tmsglen); + tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen); if ((t = (unsigned char *) xmalloc(tlen)) == NULL) return(ENOMEM); @@ -64,104 +71,192 @@ make_seal_token(context, enc_ed, seq_ed, seqnum, direction, text, token, ptr = t; - g_make_token_header((gss_OID) gss_mech_krb5, 22+tmsglen, &ptr, toktype); + g_make_token_header((gss_OID) oid, 14+cksum_size+tmsglen, &ptr, toktype); - /* for now, only generate DES integrity */ + /* 0..1 SIGN_ALG */ - ptr[0] = 0; + ptr[0] = signalg; ptr[1] = 0; + + /* 2..3 SEAL_ALG or Filler */ - /* SEAL_ALG, or filler */ - - if (((toktype == KG_TOK_SEAL_MSG) || - (toktype == KG_TOK_WRAP_MSG)) && encrypt) { - ptr[2] = 0; + if ((toktype == KG_TOK_SEAL_MSG) && encrypt) { + ptr[2] = sealalg; ptr[3] = 0; } else { + /* No seal */ ptr[2] = 0xff; ptr[3] = 0xff; } - /* filler */ + /* 4..5 Filler */ ptr[4] = 0xff; ptr[5] = 0xff; /* pad the plaintext, encrypt if needed, and stick it in the token */ - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) { + /* initialize the the cksum and allocate the contents buffer */ + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + md5cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5); + if ((md5cksum.contents = (krb5_octet *) xmalloc(md5cksum.length)) == NULL) { + return(ENOMEM); + } + + if (toktype == KG_TOK_SEAL_MSG) { unsigned char *plain; unsigned char pad; - if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { - xfree(t); - return(ENOMEM); - } + if (!bigend || encrypt) { + if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { + xfree(md5cksum.contents); + xfree(t); + return(ENOMEM); + } - if (code = kg_make_confounder(enc_ed, plain)) { - xfree(plain); - xfree(t); - return(code); - } + if (code = kg_make_confounder(enc_ed, plain)) { + xfree(plain); + xfree(md5cksum.contents); + xfree(t); + return(code); + } - memcpy(plain+8, text->value, text->length); + memcpy(plain+conflen, text->value, text->length); - pad = 8-(text->length%8); + /* XXX 8 is DES cblock size */ + pad = 8-(text->length%8); - memset(plain+8+text->length, pad, pad); + memset(plain+conflen+text->length, pad, pad); + } else { + /* plain is never used in the bigend && !encrypt case */ + plain = NULL; + } if (encrypt) { - if (code = kg_encrypt(enc_ed, NULL, (krb5_pointer) plain, - (krb5_pointer) (ptr+22), tmsglen)) { - xfree(plain); + if (code = kg_encrypt(context, enc_ed, NULL, (krb5_pointer) plain, + (krb5_pointer) (ptr+cksum_size+14), tmsglen)) { + if (plain) + xfree(plain); + xfree(md5cksum.contents); xfree(t); return(code); } } else { if (bigend) - memcpy(ptr+22, text->value, text->length); + memcpy(ptr+14+cksum_size, text->value, text->length); else - memcpy(ptr+22, plain, tmsglen); + memcpy(ptr+14+cksum_size, plain, tmsglen); } /* compute the checksum */ - krb5_MD5Init(&md5); - krb5_MD5Update(&md5, (unsigned char *) ptr-2, 8); + /* 8 = head of token body as specified by mech spec */ + if (! (data_ptr = + (char *) xmalloc(8 + (bigend ? text->length : tmsglen)))) { + if (plain) + xfree(plain); + xfree(md5cksum.contents); + xfree(t); + return(ENOMEM); + } + (void) memcpy(data_ptr, ptr-2, 8); if (bigend) - krb5_MD5Update(&md5, text->value, text->length); + (void) memcpy(data_ptr+8, text->value, text->length); else - krb5_MD5Update(&md5, plain, tmsglen); - krb5_MD5Final(&md5); + (void) memcpy(data_ptr+8, plain, tmsglen); + code = krb5_calculate_checksum(context, md5cksum.checksum_type, data_ptr, + 8 + (bigend ? text->length : tmsglen), + 0, 0, &md5cksum); + xfree(data_ptr); + + if (code) { + if (plain) + xfree(plain); + xfree(md5cksum.contents); + xfree(t); + return(code); + memcpy(ptr+14+cksum_size, plain, tmsglen); + } - xfree(plain); + if (plain) + xfree(plain); } else { /* compute the checksum */ - krb5_MD5Init(&md5); - krb5_MD5Update(&md5, (unsigned char *) ptr-2, 8); - krb5_MD5Update(&md5, text->value, text->length); - krb5_MD5Final(&md5); + if (! (data_ptr = (char *) xmalloc(8 + text->length))) { + xfree(md5cksum.contents); + xfree(t); + return(ENOMEM); + } + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, text->value, text->length); + code = krb5_calculate_checksum(context, md5cksum.checksum_type, data_ptr, + 8 + text->length, + 0, 0, &md5cksum); + xfree(data_ptr); + if (code) { + xfree(md5cksum.contents); + xfree(t); + return(code); + } } - /* XXX this depends on the key being a single-des key, but that's - all that kerberos supports right now */ - - desmac.length = sizeof(cbc_checksum); - desmac.contents = cbc_checksum; - if (code = krb5_calculate_checksum(context, CKSUMTYPE_DESCBC, md5.digest, 16, - seq_ed->key->contents, - seq_ed->key->length, - &desmac)) { - xfree(t); - return(code); + switch(signalg) { + case 0: + case 3: + +#if 0 + /* XXX this depends on the key being a single-des key */ + + /* DES CBC doesn't use a zero IV like it should in some + krb5 implementations (beta5+). So we just do the + DES encryption the long way, and keep the last block + as the MAC */ + + /* initialize the the cksum and allocate the contents buffer */ + cksum.checksum_type = CKSUMTYPE_DESCBC; + cksum.length = krb5_checksum_size(context, CKSUMTYPE_DESCBC); + if ((cksum.contents = (krb5_octet *) xmalloc(cksum.length)) == NULL) + return(ENOMEM); + + if (code = krb5_calculate_checksum(context, cksum.checksum_type, + md5cksum.contents, 16, + seq_ed->key->contents, + seq_ed->key->length, + &cksum)) { + xfree(cksum.contents); + xfree(md5cksum.contents); + xfree(t); + return(code); + } + + memcpy(ptr+14, cksum.contents, 8); + + xfree(cksum.contents); +#else + if (code = kg_encrypt(context, seq_ed, + (g_OID_equal(oid, gss_mech_krb5_old) ? + seq_ed->key->contents : NULL), + md5cksum.contents, md5cksum.contents, 16)) { + xfree(md5cksum.contents); + xfree(t); + return code; + } + + cksum.length = cksum_size; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + memcpy(ptr+14, cksum.contents, cksum.length); +#endif + + break; } - memcpy(ptr+14, desmac.contents, 8); + xfree(md5cksum.contents); /* create the seq_num */ - if (code = kg_make_seq_num(seq_ed, direction?0:0xff, *seqnum, + if (code = kg_make_seq_num(context, seq_ed, direction?0:0xff, *seqnum, ptr+14, ptr+6)) { xfree(t); return(code); @@ -227,60 +322,16 @@ kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req, if (code = make_seal_token(context, &ctx->enc, &ctx->seq, &ctx->seq_send, ctx->initiate, input_message_buffer, output_message_buffer, - conf_req_flag, toktype, ctx->big_endian)) { + ctx->signalg, ctx->cksum_size, ctx->sealalg, + conf_req_flag, toktype, ctx->big_endian, + ctx->mech_used)) { *minor_status = code; return(GSS_S_FAILURE); } - if (((toktype == KG_TOK_SEAL_MSG) || - (toktype == KG_TOK_WRAP_MSG)) && conf_state) { + if ((toktype == KG_TOK_SEAL_MSG) && conf_state) *conf_state = conf_req_flag; - } *minor_status = 0; return((ctx->endtime < now)?GSS_S_CONTEXT_EXPIRED:GSS_S_COMPLETE); } - -OM_uint32 -kg_seal_size(context, minor_status, context_handle, conf_req_flag, qop_req, - output_size, input_size) - krb5_context context; - OM_uint32 *minor_status; - gss_ctx_id_t context_handle; - int conf_req_flag; - gss_qop_t qop_req; - OM_uint32 output_size; - OM_uint32 *input_size; -{ - krb5_gss_ctx_id_rec *ctx; - OM_uint32 cfsize; - OM_uint32 ohlen; - - /* only default qop is allowed */ - if (qop_req != GSS_C_QOP_DEFAULT) { - *minor_status = (OM_uint32) G_UNKNOWN_QOP; - return(GSS_S_FAILURE); - } - - /* validate the context handle */ - if (! kg_validate_ctx_id(context_handle)) { - *minor_status = (OM_uint32) G_VALIDATE_FAILED; - return(GSS_S_NO_CONTEXT); - } - - ctx = (krb5_gss_ctx_id_rec *) context_handle; - if (! ctx->established) { - *minor_status = KG_CTX_INCOMPLETE; - return(GSS_S_NO_CONTEXT); - } - - /* Calculate the token size and subtract that from the output size */ - cfsize = (conf_req_flag) ? kg_confounder_size(&ctx->enc) : 0; - ohlen = g_token_size((gss_OID) gss_mech_krb5, (unsigned int) cfsize + 22); - - /* Cannot have trailer length that will cause us to pad over our length */ - *input_size = (output_size - ohlen) & (~7); - *minor_status = 0; - return(GSS_S_COMPLETE); -} - diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c index 1b4288c0c..70d2d4d7b 100644 --- a/src/lib/gssapi/krb5/k5unseal.c +++ b/src/lib/gssapi/krb5/k5unseal.c @@ -22,7 +22,10 @@ #include "gssapiP_krb5.h" #include -#include "rsa-md5.h" + +/* + * $Id$ + */ /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX conf_state is only valid if SEAL. @@ -44,19 +47,25 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, krb5_error_code code; int bodysize; int tmsglen; + int conflen; int signalg; int sealalg; gss_buffer_desc token; unsigned char *ptr; + krb5_checksum cksum; krb5_checksum desmac; - krb5_octet cbc_checksum[KRB5_MIT_DES_KEYSIZE]; - krb5_MD5_CTX md5; - unsigned char *cksum; + krb5_checksum md5cksum; + char *data_ptr; krb5_timestamp now; unsigned char *plain; + int cksum_len; int plainlen; + int err; + int direction; + krb5_int32 seqnum; + OM_uint32 retval; - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) { + if (toktype == KG_TOK_SEAL_MSG) { message_buffer->length = 0; message_buffer->value = NULL; } @@ -80,73 +89,109 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, ptr = (unsigned char *) input_token_buffer->value; - if (! g_verify_token_header((gss_OID) gss_mech_krb5, &bodysize, - &ptr, toktype, input_token_buffer->length)) { - *minor_status = 0; + if (err = g_verify_token_header((gss_OID) ctx->mech_used, &bodysize, + &ptr, toktype, + input_token_buffer->length)) { + *minor_status = err; return(GSS_S_DEFECTIVE_TOKEN); } - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) - tmsglen = bodysize-22; - /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1]<<8); sealalg = ptr[2] + (ptr[3]<<8); - if (((signalg != 0) && (signalg != 1)) || - (((toktype != KG_TOK_SEAL_MSG) && - (toktype != KG_TOK_WRAP_MSG)) && (sealalg != 0xffff)) || - (((toktype == KG_TOK_SEAL_MSG) || - (toktype == KG_TOK_WRAP_MSG)) && - ((sealalg != 0xffff) && (sealalg != 0))) || - (ptr[4] != 0xff) || - (ptr[5] != 0xff)) { - *minor_status = 0; - return(GSS_S_DEFECTIVE_TOKEN); + /* Sanity checks */ + + if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; } + if ((toktype != KG_TOK_SEAL_MSG) && + (sealalg != 0xffff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* in the current spec, there is only one valid seal algorithm per + key type, so a simple comparison is ok */ + + if ((toktype == KG_TOK_SEAL_MSG) && + !((sealalg == 0xffff) || + (sealalg == ctx->sealalg))) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + /* there are several mappings of seal algorithms to sign algorithms, + but few enough that we can try them all. */ + + if (((ctx->sealalg == 0) && + (signalg > 1)) || + ((ctx->sealalg == 1) && + (signalg != 3))) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + + switch (signalg) { + case 0: + case 1: + cksum_len = 8; + break; + case 3: + cksum_len = 16; + break; + } + + if (toktype == KG_TOK_SEAL_MSG) + tmsglen = bodysize-(14+cksum_len); + /* get the token parameters */ /* decode the message, if SEAL */ - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) { - if (sealalg == 0) { + if (toktype == KG_TOK_SEAL_MSG) { + if (sealalg != 0xffff) { if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) { *minor_status = ENOMEM; return(GSS_S_FAILURE); } - if (code = kg_decrypt(&ctx->enc, NULL, ptr+22, plain, tmsglen)) { + if (code = kg_decrypt(context, &ctx->enc, NULL, + ptr+14+cksum_len, plain, tmsglen)) { xfree(plain); *minor_status = code; return(GSS_S_FAILURE); } } else { - plain = ptr+22; + plain = ptr+14+cksum_len; } plainlen = tmsglen; - if (sealalg && ctx->big_endian) + if ((sealalg == 0xffff) && ctx->big_endian) { token.length = tmsglen; - else - token.length = tmsglen - 8 - plain[tmsglen-1]; + } else { + conflen = kg_confounder_size(&ctx->enc); + token.length = tmsglen - conflen - plain[tmsglen-1]; + } if (token.length) { - if ((token.value = xmalloc(token.length)) == NULL) { - if (sealalg == 0) + if ((token.value = (void *) xmalloc(token.length)) == NULL) { + if (sealalg != 0xffff) xfree(plain); *minor_status = ENOMEM; return(GSS_S_FAILURE); } - if (sealalg && ctx->big_endian) + if ((sealalg == 0xffff) && ctx->big_endian) memcpy(token.value, plain, token.length); else - memcpy(token.value, plain+8, token.length); + memcpy(token.value, plain+conflen, token.length); } - } else if ((toktype == KG_TOK_SIGN_MSG) || (toktype == KG_TOK_MIC_MSG)) { + } else if (toktype == KG_TOK_SIGN_MSG) { token = *message_buffer; plain = token.value; plainlen = token.length; @@ -159,81 +204,184 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, /* compute the checksum of the message */ - if (signalg == 0) { + /* initialize the the cksum and allocate the contents buffer */ + md5cksum.checksum_type = CKSUMTYPE_RSA_MD5; + md5cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5); + if ((md5cksum.contents = (krb5_octet *) xmalloc(md5cksum.length)) == NULL) { + if (sealalg != 0xffff) + xfree(plain); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + switch (signalg) { + case 0: + case 3: /* compute the checksum of the message */ - krb5_MD5Init(&md5); - krb5_MD5Update(&md5, (unsigned char *) ptr-2, 8); + /* 8 = bytes of token body to be checksummed according to spec */ + + if (! (data_ptr = (void *) + xmalloc(8 + (ctx->big_endian ? token.length : plainlen)))) { + xfree(md5cksum.contents); + if (sealalg != 0xffff) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + (void) memcpy(data_ptr, ptr-2, 8); + if (ctx->big_endian) - krb5_MD5Update(&md5, token.value, token.length); + (void) memcpy(data_ptr+8, token.value, token.length); else - krb5_MD5Update(&md5, plain, plainlen); - krb5_MD5Final(&md5); - - if (sealalg == 0) - xfree(plain); + (void) memcpy(data_ptr+8, plain, plainlen); + + code = krb5_calculate_checksum(context, md5cksum.checksum_type, + data_ptr, 8 + + (ctx->big_endian ? token.length : + plainlen), 0, 0, &md5cksum); + xfree(data_ptr); + + if (code) { + xfree(md5cksum.contents); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } +#if 0 /* XXX this depends on the key being a single-des key, but that's all that kerberos supports right now */ - desmac.length = sizeof(cbc_checksum); - desmac.contents = cbc_checksum; - if (code = krb5_calculate_checksum(context, CKSUMTYPE_DESCBC, md5.digest, - 16, ctx->seq.key->contents, + + /* initialize the the cksum and allocate the contents buffer */ + cksum.checksum_type = CKSUMTYPE_DESCBC; + cksum.length = krb5_checksum_size(context, CKSUMTYPE_DESCBC); + if ((cksum.contents = (krb5_octet *) xmalloc(cksum.length)) == NULL) { + xfree(md5cksum.contents); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); + } + + if (code = krb5_calculate_checksum(context, cksum.checksum_type, + md5cksum.contents, 16, + ctx->seq.key->contents, ctx->seq.key->length, - &desmac)) { - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) + &cksum)) { + xfree(cksum.contents); + xfree(md5cksum.contents); + if (toktype == KG_TOK_SEAL_MSG) xfree(token.value); *minor_status = code; return(GSS_S_FAILURE); } - cksum = desmac.contents; - } else { - if (! ctx->seed_init) { - if (code = kg_make_seed(ctx->subkey, ctx->seed)) { - if (sealalg == 0) + code = memcmp(cksum.contents, ptr+14, cksum.length); + + xfree(cksum.contents); +#else + if (code = kg_encrypt(context, &ctx->seq, + (g_OID_equal(ctx->mech_used, gss_mech_krb5_old) ? + ctx->seq.key->contents : NULL), + md5cksum.contents, md5cksum.contents, 16)) { + xfree(md5cksum.contents); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = code; + return GSS_S_FAILURE; + } + + if (signalg == 0) + cksum.length = 8; + else + cksum.length = 16; + cksum.contents = md5cksum.contents + 16 - cksum.length; + + code = memcmp(cksum.contents, ptr+14, cksum.length); +#endif + break; + + case 1: + if (!ctx->seed_init && + (code = kg_make_seed(context, ctx->subkey, ctx->seed))) { + xfree(md5cksum.contents); + if (sealalg != 0xffff) xfree(plain); - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) + if (toktype == KG_TOK_SEAL_MSG) xfree(token.value); - *minor_status = code; - return(GSS_S_FAILURE); - } - ctx->seed_init = 1; + *minor_status = code; + return GSS_S_FAILURE; + } + + if (! (data_ptr = (void *) + xmalloc(sizeof(ctx->seed) + 8 + + (ctx->big_endian ? token.length : plainlen)))) { + xfree(md5cksum.contents); + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = ENOMEM; + return(GSS_S_FAILURE); } - - krb5_MD5Init(&md5); - krb5_MD5Update(&md5, ctx->seed, sizeof(ctx->seed)); - krb5_MD5Update(&md5, (unsigned char *) ptr-2, 8); + (void) memcpy(data_ptr, ptr-2, 8); + (void) memcpy(data_ptr+8, ctx->seed, sizeof(ctx->seed)); if (ctx->big_endian) - krb5_MD5Update(&md5, token.value, token.length); + (void) memcpy(data_ptr+8+sizeof(ctx->seed), + token.value, token.length); else - krb5_MD5Update(&md5, plain, plainlen); - krb5_MD5Final(&md5); + (void) memcpy(data_ptr+8+sizeof(ctx->seed), + plain, plainlen); + code = krb5_calculate_checksum(context, md5cksum.checksum_type, + data_ptr, 8 + sizeof(ctx->seed) + + (ctx->big_endian ? token.length : + plainlen), 0, 0, &md5cksum); + + xfree(data_ptr); + + if (code) { + xfree(md5cksum.contents); + if (sealalg == 0) + xfree(plain); + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = code; + return(GSS_S_FAILURE); + } - if (sealalg == 0) - xfree(plain); + code = memcmp(md5cksum.contents, ptr+14, 8); - cksum = md5.digest; + default: + *minor_status = 0; + return(GSS_S_DEFECTIVE_TOKEN); } - + + xfree(md5cksum.contents); + if (sealalg != 0xffff) + xfree(plain); + /* compare the computed checksum against the transmitted checksum */ - if (memcmp(cksum, ptr+14, 8) != 0) { - if ((toktype == KG_TOK_SEAL_MSG) || (toktype == KG_TOK_WRAP_MSG)) + if (code) { + if (toktype == KG_TOK_SEAL_MSG) xfree(token.value); *minor_status = 0; return(GSS_S_BAD_SIG); } + - /* XXX this is where the seq_num check would go */ - /* it got through unscathed. Make sure the context is unexpired */ - if ((toktype == KG_TOK_SEAL_MSG) || (toktype = KG_TOK_WRAP_MSG)) + if (toktype == KG_TOK_SEAL_MSG) *message_buffer = token; if (conf_state) - *conf_state = (sealalg == 0); + *conf_state = (sealalg != 0xffff); if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; @@ -248,8 +396,28 @@ kg_unseal(context, minor_status, context_handle, input_token_buffer, return(GSS_S_CONTEXT_EXPIRED); } - /* success */ + /* do sequencing checks */ + + if (code = kg_get_seq_num(context, &(ctx->seq), ptr+14, ptr+6, &direction, + &seqnum)) { + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = code; + return(GSS_S_BAD_SIG); + } + + if ((ctx->initiate && direction != 0xff) || + (!ctx->initiate && direction != 0)) { + if (toktype == KG_TOK_SEAL_MSG) + xfree(token.value); + *minor_status = G_BAD_DIRECTION; + return(GSS_S_BAD_SIG); + } + + retval = g_order_check(&(ctx->seqstate), seqnum); + + /* success or ordering violation */ *minor_status = 0; - return(GSS_S_COMPLETE); + return(retval); } diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c new file mode 100644 index 000000000..f22e9d633 --- /dev/null +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -0,0 +1,538 @@ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id$ + */ + +#include "gssapiP_krb5.h" + +OM_uint32 INTERFACE +gss_accept_sec_context(minor_status, context_handle, verifier_cred_handle, + input_token, input_chan_bindings, src_name, mech_type, + output_token, ret_flags, time_rec, delegated_cred_handle) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_cred_id_t verifier_cred_handle; + gss_buffer_t input_token; + gss_channel_bindings_t input_chan_bindings; + gss_name_t *src_name; + gss_OID *mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; + gss_cred_id_t *delegated_cred_handle; +{ + return(krb5_gss_accept_sec_context(minor_status, + context_handle, + verifier_cred_handle, + input_token, + input_chan_bindings, + src_name, + mech_type, + output_token, + ret_flags, + time_rec, + delegated_cred_handle)); +} + +OM_uint32 INTERFACE +gss_acquire_cred(minor_status, desired_name, time_req, desired_mechs, + cred_usage, output_cred_handle, actual_mechs, time_rec) + OM_uint32 *minor_status; + gss_name_t desired_name; + OM_uint32 time_req; + gss_OID_set desired_mechs; + gss_cred_usage_t cred_usage; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *time_rec; +{ + return(krb5_gss_acquire_cred(minor_status, + desired_name, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_add_cred(minor_status, input_cred_handle, desired_name, desired_mech, + cred_usage, initiator_time_req, acceptor_time_req, + output_cred_handle, actual_mechs, initiator_time_rec, + acceptor_time_rec) + OM_uint32 *minor_status; + gss_cred_id_t input_cred_handle; + gss_name_t desired_name; + gss_OID desired_mech; + gss_cred_usage_t cred_usage; + OM_uint32 initiator_time_req; + OM_uint32 acceptor_time_req; + gss_cred_id_t *output_cred_handle; + gss_OID_set *actual_mechs; + OM_uint32 *initiator_time_rec; + OM_uint32 *acceptor_time_rec; +{ + return(krb5_gss_add_cred(minor_status, input_cred_handle, desired_name, + desired_mech, cred_usage, initiator_time_req, + acceptor_time_req, output_cred_handle, + actual_mechs, initiator_time_rec, + acceptor_time_rec)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_add_oid_set_member(minor_status, member_oid, oid_set) + OM_uint32 *minor_status; + gss_OID member_oid; + gss_OID_set *oid_set; +{ + return(generic_gss_add_oid_set_member(minor_status, member_oid, oid_set)); +} + +OM_uint32 INTERFACE +gss_compare_name(minor_status, name1, name2, name_equal) + OM_uint32 *minor_status; + gss_name_t name1; + gss_name_t name2; + int *name_equal; +{ + return(krb5_gss_compare_name(minor_status, name1, + name2, name_equal)); +} + +OM_uint32 INTERFACE +gss_context_time(minor_status, context_handle, time_rec) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + OM_uint32 *time_rec; +{ + return(krb5_gss_context_time(minor_status, context_handle, + time_rec)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_create_empty_oid_set(minor_status, oid_set) + OM_uint32 *minor_status; + gss_OID_set *oid_set; +{ + return(generic_gss_create_empty_oid_set(minor_status, oid_set)); +} + +OM_uint32 INTERFACE +gss_delete_sec_context(minor_status, context_handle, output_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t output_token; +{ + return(krb5_gss_delete_sec_context(minor_status, + context_handle, output_token)); +} + +OM_uint32 INTERFACE +gss_display_name(minor_status, input_name, output_name_buffer, output_name_type) + OM_uint32 *minor_status; + gss_name_t input_name; + gss_buffer_t output_name_buffer; + gss_OID *output_name_type; +{ + return(krb5_gss_display_name(minor_status, input_name, + output_name_buffer, output_name_type)); +} + +OM_uint32 INTERFACE +gss_display_status(minor_status, status_value, status_type, + mech_type, message_context, status_string) + OM_uint32 *minor_status; + OM_uint32 status_value; + int status_type; + gss_OID mech_type; + OM_uint32 *message_context; + gss_buffer_t status_string; +{ + return(krb5_gss_display_status(minor_status, status_value, + status_type, mech_type, message_context, + status_string)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_export_sec_context(minor_status, context_handle, interprocess_token) + OM_uint32 *minor_status; + gss_ctx_id_t *context_handle; + gss_buffer_t interprocess_token; +{ + return(krb5_gss_export_sec_context(minor_status, + context_handle, + interprocess_token)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_get_mic(minor_status, context_handle, qop_req, + message_buffer, message_token) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_qop_t qop_req; + gss_buffer_t message_buffer; + gss_buffer_t message_token; +{ + return(krb5_gss_get_mic(minor_status, context_handle, + qop_req, message_buffer, message_token)); +} + +OM_uint32 INTERFACE +gss_import_name(minor_status, input_name_buffer, input_name_type, output_name) + OM_uint32 *minor_status; + gss_buffer_t input_name_buffer; + gss_OID input_name_type; + gss_name_t *output_name; +{ + return(krb5_gss_import_name(minor_status, input_name_buffer, + input_name_type, output_name)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_import_sec_context(minor_status, interprocess_token, context_handle) + OM_uint32 *minor_status; + gss_buffer_t interprocess_token; + gss_ctx_id_t *context_handle; +{ + return(krb5_gss_import_sec_context(minor_status, + interprocess_token, + context_handle)); +} + +OM_uint32 INTERFACE +gss_indicate_mechs(minor_status, mech_set) + OM_uint32 *minor_status; + gss_OID_set *mech_set; +{ + return(krb5_gss_indicate_mechs(minor_status, mech_set)); +} + +OM_uint32 INTERFACE +gss_init_sec_context(minor_status, claimant_cred_handle, context_handle, + target_name, mech_type, req_flags, time_req, + input_chan_bindings, input_token, actual_mech_type, + output_token, ret_flags, time_rec) + OM_uint32 *minor_status; + gss_cred_id_t claimant_cred_handle; + gss_ctx_id_t *context_handle; + gss_name_t target_name; + gss_OID mech_type; + OM_uint32 req_flags; + OM_uint32 time_req; + gss_channel_bindings_t input_chan_bindings; + gss_buffer_t input_token; + gss_OID *actual_mech_type; + gss_buffer_t output_token; + OM_uint32 *ret_flags; + OM_uint32 *time_rec; +{ + return(krb5_gss_init_sec_context(minor_status, + claimant_cred_handle, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, input_token, + actual_mech_type, output_token, ret_flags, + time_rec)); +} + +OM_uint32 INTERFACE +gss_inquire_context(minor_status, context_handle, initiator_name, acceptor_name, + lifetime_rec, mech_type, ret_flags, + locally_initiated, open) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_name_t *initiator_name; + gss_name_t *acceptor_name; + OM_uint32 *lifetime_rec; + gss_OID *mech_type; + OM_uint32 *ret_flags; + int *locally_initiated; + int *open; +{ + return(krb5_gss_inquire_context(minor_status, context_handle, + initiator_name, acceptor_name, lifetime_rec, + mech_type, ret_flags, locally_initiated, + open)); +} + +OM_uint32 INTERFACE +gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret, + cred_usage, mechanisms) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_name_t *name; + OM_uint32 *lifetime_ret; + gss_cred_usage_t *cred_usage; + gss_OID_set *mechanisms; +{ + return(krb5_gss_inquire_cred(minor_status, cred_handle, + name, lifetime_ret, cred_usage, mechanisms)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type, name, + initiator_lifetime, acceptor_lifetime, cred_usage) + OM_uint32 *minor_status; + gss_cred_id_t cred_handle; + gss_OID mech_type; + gss_name_t *name; + OM_uint32 *initiator_lifetime; + OM_uint32 *acceptor_lifetime; + gss_cred_usage_t *cred_usage; +{ + return(krb5_gss_inquire_cred_by_mech(minor_status, cred_handle, + mech_type, name, initiator_lifetime, + acceptor_lifetime, cred_usage)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_inquire_names_for_mech(minor_status, mechanism, name_types) + OM_uint32 *minor_status; + gss_OID mechanism; + gss_OID_set *name_types; +{ + return(krb5_gss_inquire_names_for_mech(minor_status, + mechanism, + name_types)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_oid_to_str(minor_status, oid, oid_str) + OM_uint32 *minor_status; + gss_OID oid; + gss_buffer_t oid_str; +{ + return(generic_gss_oid_to_str(minor_status, oid, oid_str)); +} + +OM_uint32 INTERFACE +gss_process_context_token(minor_status, context_handle, token_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t token_buffer; +{ + return(krb5_gss_process_context_token(minor_status, + context_handle, token_buffer)); +} + +OM_uint32 INTERFACE +gss_release_cred(minor_status, cred_handle) + OM_uint32 *minor_status; + gss_cred_id_t *cred_handle; +{ + return(krb5_gss_release_cred(minor_status, cred_handle)); +} + +OM_uint32 INTERFACE +gss_release_name(minor_status, input_name) + OM_uint32 *minor_status; + gss_name_t *input_name; +{ + return(krb5_gss_release_name(minor_status, input_name)); +} + +OM_uint32 INTERFACE +gss_release_buffer(minor_status, buffer) + OM_uint32 *minor_status; + gss_buffer_t buffer; +{ + return(generic_gss_release_buffer(minor_status, + buffer)); +} + +#if 0 +/* V2 */ +OM_uint32 INTERFACE +gss_release_oid(minor_status, oid) + OM_uint32 *minor_status; + gss_OID *oid; +{ + return(krb5_gss_release_oid(minor_status, oid)); +} +#endif + +OM_uint32 INTERFACE +gss_release_oid_set(minor_status, set) + OM_uint32* minor_status; + gss_OID_set *set; +{ + return(generic_gss_release_oid_set(minor_status, set)); +} + +/* V1 only */ +OM_uint32 INTERFACE +gss_seal(minor_status, context_handle, conf_req_flag, qop_req, + input_message_buffer, conf_state, output_message_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + int qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; +{ + return(krb5_gss_seal(minor_status, context_handle, + conf_req_flag, qop_req, input_message_buffer, + conf_state, output_message_buffer)); +} + +OM_uint32 INTERFACE +gss_sign(minor_status, context_handle, + qop_req, message_buffer, + message_token) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int qop_req; + gss_buffer_t message_buffer; + gss_buffer_t message_token; +{ + return(krb5_gss_sign(minor_status, context_handle, + qop_req, message_buffer, message_token)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_verify_mic(minor_status, context_handle, + message_buffer, token_buffer, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t message_buffer; + gss_buffer_t token_buffer; + gss_qop_t *qop_state; +{ + return(krb5_gss_verify_mic(minor_status, context_handle, + message_buffer, token_buffer, qop_state)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_wrap(minor_status, context_handle, conf_req_flag, qop_req, + input_message_buffer, conf_state, output_message_buffer) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + gss_buffer_t input_message_buffer; + int *conf_state; + gss_buffer_t output_message_buffer; +{ + return(krb5_gss_wrap(minor_status, context_handle, conf_req_flag, qop_req, + input_message_buffer, conf_state, + output_message_buffer)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_str_to_oid(minor_status, oid_str, oid) + OM_uint32 *minor_status; + gss_buffer_t oid_str; + gss_OID *oid; +{ + return(generic_gss_str_to_oid(minor_status, oid_str, oid)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_test_oid_set_member(minor_status, member, set, present) + OM_uint32 *minor_status; + gss_OID member; + gss_OID_set set; + int *present; +{ + return(generic_gss_test_oid_set_member(minor_status, member, set, + present)); +} + +/* V1 only */ +OM_uint32 INTERFACE +gss_unseal(minor_status, context_handle, input_message_buffer, + output_message_buffer, conf_state, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_message_buffer; + gss_buffer_t output_message_buffer; + int *conf_state; + int *qop_state; +{ + return(krb5_gss_unseal(minor_status, context_handle, + input_message_buffer, output_message_buffer, + conf_state, qop_state)); +} + +/* V2 */ +OM_uint32 INTERFACE +gss_unwrap(minor_status, context_handle, input_message_buffer, + output_message_buffer, conf_state, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t input_message_buffer; + gss_buffer_t output_message_buffer; + int *conf_state; + gss_qop_t *qop_state; +{ + return(krb5_gss_unwrap(minor_status, context_handle, input_message_buffer, + output_message_buffer, conf_state, qop_state)); +} + +/* V1 only */ +OM_uint32 INTERFACE +gss_verify(minor_status, context_handle, message_buffer, + token_buffer, qop_state) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + gss_buffer_t message_buffer; + gss_buffer_t token_buffer; + int *qop_state; +{ + return(krb5_gss_verify(minor_status, + context_handle, + message_buffer, + token_buffer, + qop_state)); +} + +/* V2 interface */ +OM_uint32 INTERFACE +gss_wrap_size_limit(minor_status, context_handle, conf_req_flag, + qop_req, req_output_size, max_input_size) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + OM_uint32 req_output_size; + OM_uint32 *max_input_size; +{ + return(krb5_gss_wrap_size_limit(minor_status, context_handle, + conf_req_flag, qop_req, + req_output_size, max_input_size)); +} diff --git a/src/lib/gssapi/krb5/process_context_token.c b/src/lib/gssapi/krb5/process_context_token.c index 80affbd27..4639108d5 100644 --- a/src/lib/gssapi/krb5/process_context_token.c +++ b/src/lib/gssapi/krb5/process_context_token.c @@ -22,18 +22,24 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_process_context_token(ct, minor_status, context_handle, +krb5_gss_process_context_token(minor_status, context_handle, token_buffer) - void *ct; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_buffer_t token_buffer; { - krb5_context context = ct; + krb5_context context; krb5_gss_ctx_id_rec *ctx; OM_uint32 majerr; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + /* validate the context handle */ if (! kg_validate_ctx_id(context_handle)) { *minor_status = (OM_uint32) G_VALIDATE_FAILED; @@ -56,6 +62,6 @@ krb5_gss_process_context_token(ct, minor_status, context_handle, /* that's it. delete the context */ - return(krb5_gss_delete_sec_context(context, minor_status, &context_handle, + return(krb5_gss_delete_sec_context(minor_status, &context_handle, GSS_C_NO_BUFFER)); } diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c index 297699fe1..df301987b 100644 --- a/src/lib/gssapi/krb5/rel_cred.c +++ b/src/lib/gssapi/krb5/rel_cred.c @@ -23,15 +23,17 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_release_cred(ctx, minor_status, cred_handle) - void *ctx; +krb5_gss_release_cred(minor_status, cred_handle) OM_uint32 *minor_status; gss_cred_id_t *cred_handle; { - krb5_context context = ctx; + krb5_context context; krb5_gss_cred_id_t cred; krb5_error_code code1, code2; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (*cred_handle == GSS_C_NO_CREDENTIAL) return(kg_release_defcred(minor_status)); diff --git a/src/lib/gssapi/krb5/rel_name.c b/src/lib/gssapi/krb5/rel_name.c index 40ff0d2d3..56f56d060 100644 --- a/src/lib/gssapi/krb5/rel_name.c +++ b/src/lib/gssapi/krb5/rel_name.c @@ -23,13 +23,15 @@ #include "gssapiP_krb5.h" OM_uint32 -krb5_gss_release_name(ctx, minor_status, input_name) - void *ctx; +krb5_gss_release_name(minor_status, input_name) OM_uint32 *minor_status; gss_name_t *input_name; { - krb5_context context = ctx; + krb5_context context; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + if (! kg_validate_name(*input_name)) { *minor_status = (OM_uint32) G_VALIDATE_FAILED; return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME); diff --git a/src/lib/gssapi/krb5/rel_oid.c b/src/lib/gssapi/krb5/rel_oid.c index 32e697ace..d5ec7bcc7 100644 --- a/src/lib/gssapi/krb5/rel_oid.c +++ b/src/lib/gssapi/krb5/rel_oid.c @@ -73,6 +73,7 @@ krb5_gss_internal_release_oid(ct, minor_status, oid) */ if ((*oid != gss_mech_krb5) && + (*oid != gss_mech_krb5_old) && (*oid != gss_nt_krb5_name) && (*oid != gss_nt_krb5_principal)) { /* We don't know about this OID */ diff --git a/src/lib/gssapi/krb5/seal.c b/src/lib/gssapi/krb5/seal.c index ca52a60af..818de191f 100644 --- a/src/lib/gssapi/krb5/seal.c +++ b/src/lib/gssapi/krb5/seal.c @@ -22,11 +22,14 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_seal(ctx, minor_status, context_handle, conf_req_flag, +krb5_gss_seal(minor_status, context_handle, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; int conf_req_flag; @@ -35,7 +38,11 @@ krb5_gss_seal(ctx, minor_status, context_handle, conf_req_flag, int *conf_state; gss_buffer_t output_message_buffer; { - krb5_context context = ctx; + krb5_context context; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + return(kg_seal(context, minor_status, context_handle, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer, KG_TOK_SEAL_MSG)); @@ -43,10 +50,9 @@ krb5_gss_seal(ctx, minor_status, context_handle, conf_req_flag, /* V2 interface */ OM_uint32 -krb5_gss_wrap(ctx, minor_status, context_handle, conf_req_flag, +krb5_gss_wrap(minor_status, context_handle, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; int conf_req_flag; @@ -55,28 +61,13 @@ krb5_gss_wrap(ctx, minor_status, context_handle, conf_req_flag, int *conf_state; gss_buffer_t output_message_buffer; { - krb5_context context = ctx; + krb5_context context; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + return(kg_seal(context, minor_status, context_handle, conf_req_flag, (int) qop_req, input_message_buffer, conf_state, output_message_buffer, KG_TOK_WRAP_MSG)); } -/* V2 interface */ -OM_uint32 -krb5_gss_wrap_size_limit(ctx, minor_status, context_handle, conf_req_flag, - qop_req, req_output_size, max_input_size) - void *ctx; - OM_uint32 *minor_status; - gss_ctx_id_t context_handle; - int conf_req_flag; - gss_qop_t qop_req; - OM_uint32 req_output_size; - OM_uint32 *max_input_size; -{ - krb5_context context = ctx; - - /* XXX - should just put this in k5seal.c */ - return(kg_seal_size(context, minor_status, context_handle, conf_req_flag, - qop_req, req_output_size, max_input_size)); -} diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c index 5acfd5c67..2a8cd0dd0 100644 --- a/src/lib/gssapi/krb5/ser_sctx.c +++ b/src/lib/gssapi/krb5/ser_sctx.c @@ -28,264 +28,17 @@ #include "gssapiP_krb5.h" /* - * This module contains routines to [de]serialize krb5_gss_cred_id_t, + * This module contains routines to [de]serialize * krb5_gss_enc_desc and krb5_gss_ctx_id_t. + * XXX This whole serialization abstraction is unnecessary in a + * non-messaging environment, which krb5 is. Someday, this should + * all get redone without the extra level of indirection. I've done + * some of this work here, since adding new serializers is an internal + * krb5 interface, and I won't use those. There is some more + * deobfuscation (no longer anonymizing pointers, mostly) which could + * still be done. --marc */ - -/* Windows needs these prototypes since the structure they're assigned - * into is prototyped. - */ -static krb5_error_code kg_cred_size - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - size_t *sizep)); - -static krb5_error_code kg_cred_externalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - krb5_octet **buffer, - size_t *lenremain)); - -static krb5_error_code kg_cred_internalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer *argp, - krb5_octet **buffer, - size_t *lenremain)); - -static krb5_error_code kg_enc_desc_size - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - size_t *sizep)); - -static krb5_error_code kg_enc_desc_externalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - krb5_octet **buffer, - size_t *lenremain)); - -static krb5_error_code kg_enc_desc_internalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer *argp, - krb5_octet **buffer, - size_t *lenremain)); - -static krb5_error_code kg_ctx_size - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - size_t *sizep)); - -static krb5_error_code kg_ctx_externalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer arg, - krb5_octet **buffer, - size_t *lenremain)); - -static krb5_error_code kg_ctx_internalize - PROTOTYPE((krb5_context kcontext, - krb5_pointer *argp, - krb5_octet **buffer, - size_t *lenremain)); - -/* - * Determine the size required for this krb5_gss_cred_id_t. - */ -static krb5_error_code -kg_cred_size(kcontext, arg, sizep) - krb5_context kcontext; - krb5_pointer arg; - size_t *sizep; -{ - krb5_error_code kret; - krb5_gss_cred_id_t cred; - size_t required; - - /* - * krb5_gss_cred_id_t requires: - * krb5_int32 for KG_CRED - * krb5_int32 for usage. - * krb5_int32 for tgt_expire. - * krb5_int32 for trailer. - */ - kret = EINVAL; - if ((cred = (krb5_gss_cred_id_t) arg)) { - required = 4*sizeof(krb5_int32); - kret = 0; - if (cred->princ) - kret = krb5_size_opaque(kcontext, - KV5M_PRINCIPAL, - (krb5_pointer) cred->princ, - &required); - if (!kret && cred->keytab) - kret = krb5_size_opaque(kcontext, - KV5M_KEYTAB, - (krb5_pointer) cred->keytab, - &required); - - if (!kret && cred->ccache) - kret = krb5_size_opaque(kcontext, - KV5M_CCACHE, - (krb5_pointer) cred->ccache, - &required); - if (!kret) - *sizep += required; - } - return(kret); -} - -/* - * Externalize this krb5_gss_cred_id_t. - */ -static krb5_error_code -kg_cred_externalize(kcontext, arg, buffer, lenremain) - krb5_context kcontext; - krb5_pointer arg; - krb5_octet **buffer; - size_t *lenremain; -{ - krb5_error_code kret; - krb5_gss_cred_id_t cred; - size_t required; - krb5_octet *bp; - size_t remain; - - required = 0; - bp = *buffer; - remain = *lenremain; - kret = EINVAL; - if ((cred = (krb5_gss_cred_id_t) arg)) { - kret = ENOMEM; - if (!kg_cred_size(kcontext, arg, &required) && - (required <= remain)) { - /* Our identifier */ - (void) krb5_ser_pack_int32(KG_CRED, &bp, &remain); - - /* Now static data */ - (void) krb5_ser_pack_int32((krb5_int32) cred->usage, &bp, &remain); - (void) krb5_ser_pack_int32((krb5_int32) cred->tgt_expire, - &bp, &remain); - - /* Now pack up dynamic data */ - if (cred->princ) - kret = krb5_externalize_opaque(kcontext, - KV5M_PRINCIPAL, - (krb5_pointer) cred->princ, - &bp, &remain); - else - kret = 0; - - if (!kret && cred->keytab) - kret = krb5_externalize_opaque(kcontext, - KV5M_KEYTAB, - (krb5_pointer) cred->keytab, - &bp, &remain); - - if (!kret && cred->ccache) - kret = krb5_externalize_opaque(kcontext, - KV5M_CCACHE, - (krb5_pointer) cred->ccache, - &bp, &remain); - - if (!kret) { - (void) krb5_ser_pack_int32(KG_CRED, &bp, &remain); - *buffer = bp; - *lenremain = remain; - } - } - } - return(kret); -} - -/* - * Internalize this krb5_gss_cred_id_t. - */ -static krb5_error_code -kg_cred_internalize(kcontext, argp, buffer, lenremain) - krb5_context kcontext; - krb5_pointer *argp; - krb5_octet **buffer; - size_t *lenremain; -{ - krb5_error_code kret; - krb5_gss_cred_id_t cred; - krb5_int32 ibuf; - krb5_octet *bp; - size_t remain; - - bp = *buffer; - remain = *lenremain; - kret = EINVAL; - /* Read our magic number */ - if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) - ibuf = 0; - if (ibuf == KG_CRED) { - kret = ENOMEM; - - /* Get a cred */ - if ((remain >= (2*sizeof(krb5_int32))) && - (cred = (krb5_gss_cred_id_t) - xmalloc(sizeof(krb5_gss_cred_id_rec)))) { - memset(cred, 0, sizeof(krb5_gss_cred_id_rec)); - - /* Get the static data */ - (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); - cred->usage = (int) ibuf; - (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); - cred->tgt_expire = (krb5_timestamp) ibuf; - - /* cred->princ */ - if ((kret = krb5_internalize_opaque(kcontext, - KV5M_PRINCIPAL, - (krb5_pointer *) &cred->princ, - &bp, &remain))) { - if (kret == EINVAL) - kret = 0; - } - - /* cred->keytab */ - if (!kret && - (kret = krb5_internalize_opaque(kcontext, - KV5M_KEYTAB, - (krb5_pointer *) &cred->keytab, - &bp, &remain))) { - if (kret == EINVAL) - kret = 0; - } - - /* cred->ccache */ - if (!kret && - (kret = krb5_internalize_opaque(kcontext, - KV5M_CCACHE, - (krb5_pointer *) &cred->ccache, - &bp, &remain))) { - if (kret == EINVAL) - kret = 0; - } - - /* trailer */ - if (!kret && - !(kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)) && - (ibuf == KG_CRED)) { - *buffer = bp; - *lenremain = remain; - *argp = (krb5_pointer) cred; - } - else { - if (!kret && (ibuf != KG_CRED)) - kret = EINVAL; - if (cred->ccache) - krb5_cc_close(kcontext, cred->ccache); - if (cred->keytab) - krb5_kt_close(kcontext, cred->keytab); - if (cred->princ) - krb5_free_principal(kcontext, cred->princ); - xfree(cred); - } - } - } - return(kret); -} - /* * Determine the size required for this krb5_gss_enc_desc. */ @@ -332,7 +85,7 @@ kg_enc_desc_size(kcontext, arg, sizep) } return(kret); } - + /* * Externalize this krb5_gss_enc_desc. */ @@ -387,7 +140,7 @@ kg_enc_desc_externalize(kcontext, arg, buffer, lenremain) } return(kret); } - + /* * Internalize this krb5_gss_enc_desc. */ @@ -471,27 +224,88 @@ kg_enc_desc_internalize(kcontext, argp, buffer, lenremain) } return(kret); } - + +static krb5_error_code +kg_oid_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid = (gss_OID) arg; + + (void) krb5_ser_pack_int32((krb5_int32) oid->length, + buffer, lenremain); + (void) krb5_ser_pack_bytes((krb5_octet *) oid->elements, + oid->length, buffer, lenremain); +} + +static krb5_error_code +kg_oid_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + gss_OID oid; + krb5_int32 ibuf; + + oid = (gss_OID) malloc(sizeof(gss_OID_desc)); + if (oid == NULL) + return ENOMEM; + (void) krb5_ser_unpack_int32(&ibuf, buffer, lenremain); + oid->length = ibuf; + (void) krb5_ser_unpack_bytes((krb5_octet *) oid->elements, + oid->length, buffer, lenremain); + return 0; +} + +krb5_error_code +kg_oid_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + gss_OID oid; + size_t required; + + kret = EINVAL; + if ((oid = (gss_OID) arg)) { + required = sizeof(krb5_int32); + required += oid->length; + + kret = 0; + + *sizep += required; + } + + return(kret); +} + /* - * Determine the size required for this krb5_gss_ctx_id_t. + * Determine the size required for this krb5_gss_ctx_id_rec. */ -static krb5_error_code +krb5_error_code kg_ctx_size(kcontext, arg, sizep) krb5_context kcontext; krb5_pointer arg; size_t *sizep; { krb5_error_code kret; - krb5_gss_ctx_id_t *ctx; + krb5_gss_ctx_id_rec *ctx; size_t required; /* - * krb5_gss_ctx_id_t requires: + * krb5_gss_ctx_id_rec requires: * krb5_int32 for KG_CONTEXT * krb5_int32 for initiate. * krb5_int32 for mutual. * krb5_int32 for seed_init. * sizeof(seed) for seed + * krb5_int32 for signalg. + * krb5_int32 for cksum_size. + * krb5_int32 for sealalg. * krb5_int32 for endtime. * krb5_int32 for flags. * krb5_int32 for seq_send. @@ -501,8 +315,8 @@ kg_ctx_size(kcontext, arg, sizep) * krb5_int32 for trailer. */ kret = EINVAL; - if ((ctx = (krb5_gss_ctx_id_t *) arg)) { - required = 11*sizeof(krb5_int32); + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { + required = 14*sizeof(krb5_int32); required += sizeof(ctx->seed); kret = 0; @@ -525,33 +339,30 @@ kg_ctx_size(kcontext, arg, sizep) &required); if (!kret) - kret = krb5_size_opaque(kcontext, - KG_ENC_DESC, + kret = kg_enc_desc_size(kcontext, (krb5_pointer) &ctx->enc, &required); if (!kret) - kret = krb5_size_opaque(kcontext, - KG_ENC_DESC, + kret = kg_enc_desc_size(kcontext, (krb5_pointer) &ctx->seq, &required); - if (!kret && ctx->auth_context) - kret = krb5_size_opaque(kcontext, - KV5M_AUTH_CONTEXT, - (krb5_pointer) ctx->auth_context, - &required); + if (!kret) + kret = kg_oid_size(kcontext, + (krb5_pointer) ctx->mech_used, + &required); if (!kret) *sizep += required; } return(kret); } - + /* - * Externalize this krb5_gss_ctx_id_t. + * Externalize this krb5_gss_ctx_id_ret. */ -static krb5_error_code +krb5_error_code kg_ctx_externalize(kcontext, arg, buffer, lenremain) krb5_context kcontext; krb5_pointer arg; @@ -559,7 +370,7 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) size_t *lenremain; { krb5_error_code kret; - krb5_gss_ctx_id_t *ctx; + krb5_gss_ctx_id_rec *ctx; size_t required; krb5_octet *bp; size_t remain; @@ -568,7 +379,7 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) bp = *buffer; remain = *lenremain; kret = EINVAL; - if ((ctx = (krb5_gss_ctx_id_t *) arg)) { + if ((ctx = (krb5_gss_ctx_id_rec *) arg)) { kret = ENOMEM; if (!kg_ctx_size(kcontext, arg, &required) && (required <= remain)) { @@ -585,6 +396,12 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) (void) krb5_ser_pack_bytes((krb5_octet *) ctx->seed, sizeof(ctx->seed), &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->signalg, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->cksum_size, + &bp, &remain); + (void) krb5_ser_pack_int32((krb5_int32) ctx->sealalg, + &bp, &remain); (void) krb5_ser_pack_int32((krb5_int32) ctx->endtime, &bp, &remain); (void) krb5_ser_pack_int32((krb5_int32) ctx->flags, @@ -601,6 +418,10 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) /* Now dynamic data */ kret = 0; + if (!kret && ctx->mech_used) + kret = kg_oid_externalize(kcontext, ctx->mech_used, + &bp, &remain); + if (!kret && ctx->here) kret = krb5_externalize_opaque(kcontext, KV5M_PRINCIPAL, @@ -620,23 +441,15 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) &bp, &remain); if (!kret) - kret = krb5_externalize_opaque(kcontext, - KG_ENC_DESC, + kret = kg_enc_desc_externalize(kcontext, (krb5_pointer) &ctx->enc, &bp, &remain); if (!kret) - kret = krb5_externalize_opaque(kcontext, - KG_ENC_DESC, + kret = kg_enc_desc_externalize(kcontext, (krb5_pointer) &ctx->seq, &bp, &remain); - if (!kret && ctx->auth_context) - kret = krb5_externalize_opaque(kcontext, - KV5M_AUTH_CONTEXT, - (krb5_pointer)ctx->auth_context, - &bp, &remain); - if (!kret) { (void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain); *buffer = bp; @@ -646,11 +459,11 @@ kg_ctx_externalize(kcontext, arg, buffer, lenremain) } return(kret); } - + /* * Internalize this krb5_gss_ctx_id_t. */ -static krb5_error_code +krb5_error_code kg_ctx_internalize(kcontext, argp, buffer, lenremain) krb5_context kcontext; krb5_pointer *argp; @@ -658,7 +471,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) size_t *lenremain; { krb5_error_code kret; - krb5_gss_ctx_id_t *ctx; + krb5_gss_ctx_id_rec *ctx; krb5_int32 ibuf; krb5_octet *bp; size_t remain; @@ -675,9 +488,9 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) /* Get a context */ if ((remain >= ((10*sizeof(krb5_int32))+sizeof(ctx->seed))) && - (ctx = (krb5_gss_ctx_id_t *) - xmalloc(sizeof(krb5_gss_ctx_id_t)))) { - memset(ctx, 0, sizeof(krb5_gss_ctx_id_t)); + (ctx = (krb5_gss_ctx_id_rec *) + xmalloc(sizeof(krb5_gss_ctx_id_rec)))) { + memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec)); /* Get static data */ (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); @@ -690,9 +503,15 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) sizeof(ctx->seed), &bp, &remain); (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->signalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->cksum_size = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); + ctx->sealalg = (int) ibuf; + (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); ctx->endtime = (krb5_timestamp) ibuf; (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); - ctx->flags = (krb5_timestamp) ibuf; + ctx->flags = (krb5_flags) ibuf; (void) krb5_ser_unpack_int32(&ctx->seq_send, &bp, &remain); (void) krb5_ser_unpack_int32(&ctx->seq_recv, &bp, &remain); (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); @@ -700,6 +519,11 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); ctx->big_endian = (int) ibuf; + if ((kret = kg_oid_internalize(kcontext, &ctx->mech_used, &bp, + &remain))) { + if (kret == EINVAL) + kret = 0; + } /* Now get substructure data */ if ((kret = krb5_internalize_opaque(kcontext, KV5M_PRINCIPAL, @@ -725,8 +549,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) kret = 0; } if (!kret) { - if ((kret = krb5_internalize_opaque(kcontext, - KG_ENC_DESC, + if ((kret = kg_enc_desc_internalize(kcontext, (krb5_pointer *) &edp, &bp, &remain))) { if (kret == EINVAL) @@ -738,8 +561,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) } } if (!kret) { - if ((kret = krb5_internalize_opaque(kcontext, - KG_ENC_DESC, + if ((kret = kg_enc_desc_internalize(kcontext, (krb5_pointer *) &edp, &bp, &remain))) { if (kret == EINVAL) @@ -750,15 +572,6 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) xfree(edp); } } - if (!kret && - (kret = krb5_internalize_opaque(kcontext, - KV5M_AUTH_CONTEXT, - (krb5_pointer *) - &ctx->auth_context, - &bp, &remain))) { - if (kret == EINVAL) - kret = 0; - } /* Get trailer */ if (!kret && @@ -771,8 +584,6 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) else { if (!kret && (ibuf != KG_CONTEXT)) kret = EINVAL; - if (ctx->auth_context) - krb5_auth_con_free(kcontext, ctx->auth_context); if (ctx->seq.eblock.key) krb5_free_keyblock(kcontext, ctx->seq.eblock.key); if (ctx->seq.eblock.priv && ctx->seq.eblock.priv_size) @@ -797,36 +608,3 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain) } return(kret); } - -static const krb5_ser_entry kg_cred_ser_entry = { - KG_CRED, /* Type */ - kg_cred_size, /* Sizer routine */ - kg_cred_externalize, /* Externalize routine */ - kg_cred_internalize /* Internalize routine */ -}; -static const krb5_ser_entry kg_enc_desc_ser_entry = { - KG_ENC_DESC, /* Type */ - kg_enc_desc_size, /* Sizer routine */ - kg_enc_desc_externalize, /* Externalize routine */ - kg_enc_desc_internalize /* Internalize routine */ -}; -static const krb5_ser_entry kg_ctx_ser_entry = { - KG_CONTEXT, /* Type */ - kg_ctx_size, /* Sizer routine */ - kg_ctx_externalize, /* Externalize routine */ - kg_ctx_internalize /* Internalize routine */ -}; - -krb5_error_code -kg_ser_context_init(kcontext) - krb5_context kcontext; -{ - krb5_error_code kret; - - kret = krb5_register_serializer(kcontext, &kg_cred_ser_entry); - if (!kret) - kret = krb5_register_serializer(kcontext, &kg_enc_desc_ser_entry); - if (!kret) - kret = krb5_register_serializer(kcontext, &kg_ctx_ser_entry); - return(kret); -} diff --git a/src/lib/gssapi/krb5/sign.c b/src/lib/gssapi/krb5/sign.c index c3b6ebf4b..0177f40d4 100644 --- a/src/lib/gssapi/krb5/sign.c +++ b/src/lib/gssapi/krb5/sign.c @@ -22,19 +22,25 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_sign(ctx, minor_status, context_handle, +krb5_gss_sign(minor_status, context_handle, qop_req, message_buffer, message_token) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; int qop_req; gss_buffer_t message_buffer; gss_buffer_t message_token; { - krb5_context context = ctx; + krb5_context context; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + return(kg_seal(context, minor_status, context_handle, 0, qop_req, message_buffer, NULL, message_token, KG_TOK_SIGN_MSG)); @@ -42,17 +48,19 @@ krb5_gss_sign(ctx, minor_status, context_handle, /* V2 interface */ OM_uint32 -krb5_gss_get_mic(ctx, minor_status, context_handle, qop_req, +krb5_gss_get_mic(minor_status, context_handle, qop_req, message_buffer, message_token) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_qop_t qop_req; gss_buffer_t message_buffer; gss_buffer_t message_token; { - krb5_context context = ctx; + krb5_context context; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + return(kg_seal(context, minor_status, context_handle, 0, (int) qop_req, message_buffer, NULL, message_token, KG_TOK_MIC_MSG)); diff --git a/src/lib/gssapi/krb5/unseal.c b/src/lib/gssapi/krb5/unseal.c index da71fa4f4..546521e1b 100644 --- a/src/lib/gssapi/krb5/unseal.c +++ b/src/lib/gssapi/krb5/unseal.c @@ -22,11 +22,14 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_unseal(ctx, minor_status, context_handle, +krb5_gss_unseal(minor_status, context_handle, input_message_buffer, output_message_buffer, conf_state, qop_state) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_buffer_t input_message_buffer; @@ -34,7 +37,11 @@ krb5_gss_unseal(ctx, minor_status, context_handle, int *conf_state; int *qop_state; { - krb5_context context = ctx; + krb5_context context; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + return(kg_unseal(context, minor_status, context_handle, input_message_buffer, output_message_buffer, conf_state, qop_state, KG_TOK_SEAL_MSG)); @@ -42,10 +49,9 @@ krb5_gss_unseal(ctx, minor_status, context_handle, /* V2 interface */ OM_uint32 -krb5_gss_unwrap(ctx, minor_status, context_handle, +krb5_gss_unwrap(minor_status, context_handle, input_message_buffer, output_message_buffer, conf_state, qop_state) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_buffer_t input_message_buffer; @@ -53,10 +59,13 @@ krb5_gss_unwrap(ctx, minor_status, context_handle, int *conf_state; gss_qop_t *qop_state; { - krb5_context context = ctx; + krb5_context context; OM_uint32 rstat; int qstate; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + rstat = kg_unseal(context, minor_status, context_handle, input_message_buffer, output_message_buffer, conf_state, &qstate, KG_TOK_WRAP_MSG); diff --git a/src/lib/gssapi/krb5/util_cksum.c b/src/lib/gssapi/krb5/util_cksum.c index 0b46d0e5e..ee3e1aafa 100644 --- a/src/lib/gssapi/krb5/util_cksum.c +++ b/src/lib/gssapi/krb5/util_cksum.c @@ -20,11 +20,16 @@ * PERFORMANCE OF THIS SOFTWARE. */ +/* + * $Id$ + */ + #include "gssapiP_krb5.h" #include krb5_error_code -kg_checksum_channel_bindings(cb, cksum, bigend) +kg_checksum_channel_bindings(context, cb, cksum, bigend) + krb5_context context; gss_channel_bindings_t cb; krb5_checksum *cksum; int bigend; @@ -33,20 +38,18 @@ kg_checksum_channel_bindings(cb, cksum, bigend) char *buf, *ptr; krb5_error_code code; - if (!kg_context && (code=kg_get_context())) - return code; - + /* initialize the the cksum and allocate the contents buffer */ + cksum->checksum_type = CKSUMTYPE_RSA_MD5; + cksum->length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5); + if ((cksum->contents = (krb5_octet *) xmalloc(cksum->length)) == NULL) { + free(buf); + return(ENOMEM); + } + /* generate a buffer full of zeros if no cb specified */ if (cb == GSS_C_NO_CHANNEL_BINDINGS) { - /* allocate the cksum contents buffer */ - if ((cksum->contents = (krb5_octet *) - xmalloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5))) == NULL) - return(ENOMEM); - - cksum->checksum_type = CKSUMTYPE_RSA_MD5; - memset(cksum->contents, '\0', - (cksum->length = krb5_checksum_size(kg_context, CKSUMTYPE_RSA_MD5))); + memset(cksum->contents, '\0', cksum->length); return(0); } @@ -60,13 +63,6 @@ kg_checksum_channel_bindings(cb, cksum, bigend) if ((buf = (char *) xmalloc(len)) == NULL) return(ENOMEM); - /* allocate the cksum contents buffer */ - cksum->length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5); - if ((cksum->contents = (krb5_octet *) xmalloc(cksum->length)) == NULL) { - free(buf); - return(ENOMEM); - } - /* helper macros. This code currently depends on a long being 32 bits, and htonl dtrt. */ @@ -80,7 +76,7 @@ kg_checksum_channel_bindings(cb, cksum, bigend) /* checksum the data */ - if (code = krb5_calculate_checksum(kg_context, CKSUMTYPE_RSA_MD5, + if (code = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5, buf, len, NULL, 0, cksum)) { xfree(cksum->contents); xfree(buf); diff --git a/src/lib/gssapi/krb5/util_crypt.c b/src/lib/gssapi/krb5/util_crypt.c index 89028270e..bf49de41d 100644 --- a/src/lib/gssapi/krb5/util_crypt.c +++ b/src/lib/gssapi/krb5/util_crypt.c @@ -23,6 +23,10 @@ #include "gssapiP_krb5.h" #include +/* + * $Id$ + */ + static unsigned char zeros[8] = {0,0,0,0,0,0,0,0}; int @@ -39,7 +43,7 @@ kg_make_confounder(ed, buf) krb5_gss_enc_desc *ed; unsigned char *buf; { - return(krb5_random_confounder( ed->eblock.crypto_entry->block_length, buf)); + return(krb5_random_confounder(ed->eblock.crypto_entry->block_length, buf)); } int @@ -51,7 +55,8 @@ kg_encrypt_size(ed, n) } krb5_error_code -kg_encrypt(ed, iv, in, out, length) +kg_encrypt(context, ed, iv, in, out, length) + krb5_context context; krb5_gss_enc_desc *ed; krb5_pointer iv; krb5_pointer in; @@ -59,18 +64,29 @@ kg_encrypt(ed, iv, in, out, length) int length; { krb5_error_code code; + krb5_pointer tmp; - if (!kg_context && (code=kg_get_context())) - return code; - if (! ed->processed) { - if (code = krb5_process_key(kg_context, &ed->eblock, ed->key)) + if (code = krb5_process_key(context, &ed->eblock, ed->key)) return(code); ed->processed = 1; } - if (code = krb5_encrypt(kg_context, in, out, length, &ed->eblock, - iv?iv:(krb5_pointer)zeros)) + /* this is lame. the krb5 encryption interfaces no longer allow + you to encrypt in place. perhaps this should be fixed, but + dealing here is easier for now --marc */ + + if ((tmp = (krb5_pointer) xmalloc(length)) == NULL) + return(ENOMEM); + + memcpy(tmp, in, length); + + code = krb5_encrypt(context, tmp, out, length, &ed->eblock, + iv?iv:(krb5_pointer)zeros); + + xfree(tmp); + + if (code) return(code); return(0); @@ -79,7 +95,8 @@ kg_encrypt(ed, iv, in, out, length) /* length is the length of the cleartext. */ krb5_error_code -kg_decrypt(ed, iv, in, out, length) +kg_decrypt(context, ed, iv, in, out, length) + krb5_context context; krb5_gss_enc_desc *ed; krb5_pointer iv; krb5_pointer in; @@ -90,11 +107,8 @@ kg_decrypt(ed, iv, in, out, length) int elen; char *buf; - if (!kg_context && (code=kg_get_context())) - return code; - if (! ed->processed) { - if (code = krb5_process_key(kg_context, &ed->eblock, ed->key)) + if (code = krb5_process_key(context, &ed->eblock, ed->key)) return(code); ed->processed = 1; } @@ -103,7 +117,7 @@ kg_decrypt(ed, iv, in, out, length) if ((buf = (char *) xmalloc(elen)) == NULL) return(ENOMEM); - if (code = krb5_decrypt(kg_context, in, buf, elen, &ed->eblock, + if (code = krb5_decrypt(context, in, buf, elen, &ed->eblock, iv?iv:(krb5_pointer)zeros)) { xfree(buf); return(code); diff --git a/src/lib/gssapi/krb5/util_seed.c b/src/lib/gssapi/krb5/util_seed.c index ed60922d5..14f365245 100644 --- a/src/lib/gssapi/krb5/util_seed.c +++ b/src/lib/gssapi/krb5/util_seed.c @@ -23,10 +23,15 @@ #include "gssapiP_krb5.h" #include +/* + * $Id$ + */ + static unsigned char zeros[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; krb5_error_code -kg_make_seed(key, seed) +kg_make_seed(context, key, seed) + krb5_context context; krb5_keyblock *key; unsigned char *seed; { @@ -34,10 +39,7 @@ kg_make_seed(key, seed) krb5_gss_enc_desc ed; int i; - if (!kg_context && (code=kg_get_context())) - return code; - - if (code = krb5_copy_keyblock(kg_context, key, &ed.key)) + if (code = krb5_copy_keyblock(context, key, &ed.key)) return(code); /* reverse the key bytes, as per spec */ @@ -45,13 +47,13 @@ kg_make_seed(key, seed) for (i=0; ilength; i++) ed.key->contents[i] = key->contents[key->length - 1 - i]; - krb5_use_enctype(kg_context, &ed.eblock, ENCTYPE_DES_CBC_RAW); + krb5_use_enctype(context, &ed.eblock, ENCTYPE_DES_CBC_RAW); ed.processed = 0; - code = kg_encrypt(&ed, NULL, zeros, seed, 16); + code = kg_encrypt(context, &ed, NULL, zeros, seed, 16); - krb5_finish_key(kg_context, &ed.eblock); - krb5_free_keyblock(kg_context, ed.key); + krb5_finish_key(context, &ed.eblock); + krb5_free_keyblock(context, ed.key); return(code); } diff --git a/src/lib/gssapi/krb5/util_seqnum.c b/src/lib/gssapi/krb5/util_seqnum.c index 67bcda6db..ed9293f77 100644 --- a/src/lib/gssapi/krb5/util_seqnum.c +++ b/src/lib/gssapi/krb5/util_seqnum.c @@ -22,8 +22,13 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + krb5_error_code -kg_make_seq_num(ed, direction, seqnum, cksum, buf) +kg_make_seq_num(context, ed, direction, seqnum, cksum, buf) + krb5_context context; krb5_gss_enc_desc *ed; int direction; krb5_int32 seqnum; @@ -42,5 +47,34 @@ kg_make_seq_num(ed, direction, seqnum, cksum, buf) plain[6] = direction; plain[7] = direction; - return(kg_encrypt(ed, cksum, plain, buf, 8)); + return(kg_encrypt(context, ed, cksum, plain, buf, 8)); +} + +krb5_error_code kg_get_seq_num(context, ed, cksum, buf, direction, seqnum) + krb5_context context; + krb5_gss_enc_desc *ed; + unsigned char *cksum; + unsigned char *buf; + int *direction; + krb5_int32 *seqnum; +{ + krb5_error_code code; + unsigned char plain[8]; + + if (code = kg_decrypt(context, ed, cksum, buf, plain, 8)) + return(code); + + if ((plain[4] != plain[5]) || + (plain[4] != plain[6]) || + (plain[4] != plain[7])) + return((krb5_error_code) KG_BAD_SEQ); + + *direction = plain[4]; + + *seqnum = ((plain[0]) | + (plain[1]<<8) | + (plain[2]<<16) | + (plain[3]<<24)); + + return(0); } diff --git a/src/lib/gssapi/krb5/verify.c b/src/lib/gssapi/krb5/verify.c index 33ee8fb8c..0e7305640 100644 --- a/src/lib/gssapi/krb5/verify.c +++ b/src/lib/gssapi/krb5/verify.c @@ -22,39 +22,50 @@ #include "gssapiP_krb5.h" +/* + * $Id$ + */ + OM_uint32 -krb5_gss_verify(ctx, minor_status, context_handle, +krb5_gss_verify(minor_status, context_handle, message_buffer, token_buffer, qop_state) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_buffer_t message_buffer; gss_buffer_t token_buffer; int *qop_state; { - krb5_context context = ctx; - return(kg_unseal(context, minor_status, context_handle, - token_buffer, message_buffer, - NULL, qop_state, KG_TOK_SIGN_MSG)); + krb5_context context; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + + + return(kg_unseal(context, minor_status, context_handle, + token_buffer, message_buffer, + NULL, qop_state, KG_TOK_SIGN_MSG)); } /* V2 interface */ OM_uint32 -krb5_gss_verify_mic(ctx, minor_status, context_handle, +krb5_gss_verify_mic(minor_status, context_handle, message_buffer, token_buffer, qop_state) - void *ctx; OM_uint32 *minor_status; gss_ctx_id_t context_handle; gss_buffer_t message_buffer; gss_buffer_t token_buffer; gss_qop_t *qop_state; { - krb5_context context = ctx; + krb5_context context; OM_uint32 rstat; int qstate; + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + + rstat = kg_unseal(context, minor_status, context_handle, token_buffer, message_buffer, NULL, &qstate, KG_TOK_MIC_MSG); diff --git a/src/lib/gssapi/krb5/wrap_size_limit.c b/src/lib/gssapi/krb5/wrap_size_limit.c new file mode 100644 index 000000000..8c47d0fa6 --- /dev/null +++ b/src/lib/gssapi/krb5/wrap_size_limit.c @@ -0,0 +1,75 @@ +/* + * Copyright 1993 by OpenVision Technologies, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of OpenVision not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. OpenVision makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gssapiP_krb5.h" + +/* + * $Id$ + */ + +/* V2 interface */ +OM_uint32 +krb5_gss_wrap_size_limit(minor_status, context_handle, conf_req_flag, + qop_req, req_output_size, max_input_size) + OM_uint32 *minor_status; + gss_ctx_id_t context_handle; + int conf_req_flag; + gss_qop_t qop_req; + OM_uint32 req_output_size; + OM_uint32 *max_input_size; +{ + krb5_context context; + krb5_gss_ctx_id_rec *ctx; + OM_uint32 cfsize; + OM_uint32 ohlen; + + if (GSS_ERROR(kg_get_context(minor_status, &context))) + return(GSS_S_FAILURE); + + /* only default qop is allowed */ + if (qop_req != GSS_C_QOP_DEFAULT) { + *minor_status = (OM_uint32) G_UNKNOWN_QOP; + return(GSS_S_FAILURE); + } + + /* validate the context handle */ + if (! kg_validate_ctx_id(context_handle)) { + *minor_status = (OM_uint32) G_VALIDATE_FAILED; + return(GSS_S_NO_CONTEXT); + } + + ctx = (krb5_gss_ctx_id_rec *) context_handle; + if (! ctx->established) { + *minor_status = KG_CTX_INCOMPLETE; + return(GSS_S_NO_CONTEXT); + } + + /* Calculate the token size and subtract that from the output size */ + cfsize = (conf_req_flag) ? kg_confounder_size(&ctx->enc) : 0; + ohlen = g_token_size((gss_OID) ctx->mech_used, + (unsigned int) cfsize + ctx->cksum_size + 14); + + /* Cannot have trailer length that will cause us to pad over our length */ + *max_input_size = (req_output_size - ohlen) & (~7); + *minor_status = 0; + return(GSS_S_COMPLETE); +} diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index e82cfbba0..22b8c5bdb 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -74,6 +74,17 @@ typedef struct gss_union_cred_t { /* define one of these and provide a function to return */ /* it to initialize the GSSAPI library */ +/* ultrix cc doesn't understand prototypes in structures. + we could autoconf test for this --marc */ + +#ifndef NPROTOTYPE +#if defined(__ultrix) && !defined (__GNUC__) +#define NPROTOTYPE(x) () +#else +#define NPROTOTYPE(x) PROTOTYPE(x) +#endif +#endif + /* * This is the definition of the mechs_array struct, which is used to * define the mechs array table. This table is used to indirectly diff --git a/src/lib/kadm/ChangeLog b/src/lib/kadm/ChangeLog index 6e9e4d64e..ac07e1981 100644 --- a/src/lib/kadm/ChangeLog +++ b/src/lib/kadm/ChangeLog @@ -13,6 +13,11 @@ Tue May 21 20:51:06 1996 Sam Hartman * Makefile.in (check-unix): Use KRB5_RUN_FLAGS +Sun May 12 00:46:57 1996 Marc Horowitz + + * alt_prof.c (krb5_read_realm_params): added "acl_file" variable + for the admin server. + Wed Mar 13 17:37:00 1996 Ken Raeburn * configure.in: Use AC_HEADER_STDARG. diff --git a/src/lib/kadm/alt_prof.c b/src/lib/kadm/alt_prof.c index d4512c41c..9556ac450 100644 --- a/src/lib/kadm/alt_prof.c +++ b/src/lib/kadm/alt_prof.c @@ -305,6 +305,11 @@ krb5_read_realm_params(kcontext, realm, kdcprofile, kdcenv, rparamp) if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) rparams->realm_kdc_ports = svalue; + /* Get the name of the acl file */ + hierarchy[2] = "acl_file"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_acl_file = svalue; + /* Get the value for the kadmind port */ hierarchy[2] = "kadmind_port"; if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { diff --git a/src/lib/kadm5/ChangeLog b/src/lib/kadm5/ChangeLog new file mode 100644 index 000000000..a10d351d1 --- /dev/null +++ b/src/lib/kadm5/ChangeLog @@ -0,0 +1,81 @@ +Mon Jul 22 04:17:23 1996 Marc Horowitz + + * configure.in (LIBS): add -lgen to LIBS whenever compile is + found. Solaris requires it. + + * chpass_util.c (_kadm5_chpass_principal_util): the calls to + kadm5_free_{princicpal,policy}_ent used server_handle instead of + lhandle, which caused problems in the api versioning code. + +Thu Jul 18 19:50:39 1996 Marc Horowitz + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Mon Jul 15 16:52:44 1996 Barry Jaspan + + * client_init.c (_kadm5_init_any): use krb5_get_in_tkt_keytab + instead of changing krb5_defkeyname + +Mon Jul 15 16:36:02 1996 Marc Horowitz + + * Makefile.in (CLNTOBJS), AC_REPLACE_FUNCS: check for setenv, and + link against setenv.o if it's needed. + +Fri Jul 12 15:06:48 1996 Marc Horowitz + + * svr_iters.c (glob_to_regexp:append_realm): the semantics and + code were somewhat confused. they are now fixed. + + * logger.c (HAVE_*): turn all the "#if HAVE_*" into + "#ifdef HAVE_*" + + * configure.in (AC_CHECK_FUNCS): check for the functions which + logger.c checks for. + + * svr_principal.c (kadm5_get_principal): due to the the api + versioning, it is possible for this function to be called with a + three argument prototype. in this case, do not modify mask, + because this will clobber the stack on some platforms. + + * client_principal.c (kadm5_create_principal): be more careful + about what sorts of things are referenced, passed down, and passed + back if the caller is api v1. + +Wed Jul 10 01:29:34 1996 Marc Horowitz + + * configure.in: added autoconf support + +Tue Jul 9 17:38:43 1996 Marc Horowitz + + * svr_iters.c (*_REGEXPS): rework the conditionals to operate + as functions of function symbols tested by configure. + * client_init.c (setenv, unsetenv declarations): make them the + same as the stdlib declarations, if they're going to be here at + all. + * Makefile.in: reworked to support building both libraries. this + required a bunch of changes, including some coordinating ones in + aclocal.m4 + +Tue Jul 9 16:26:26 1996 Barry Jaspan + + * svr_principal.c (kadm5_decrypt_key): add kadm5_decrypt_key + +Mon Jul 8 16:55:22 1996 Barry Jaspan + + * svr_iters.c (kadm5_get_either): append local ream to globs with + no realm + + * alt_prof.c: fix dbname, admin_dbname, and admin_lockfile to + derive from each other as in spec + + * adb_policy.c: add create_db/destroy_db + + * adb_openclose.c: add create_db/destroy_db, fix handling of + permanent locks, handle multiple lock files via static linked list + + * adb.h: update create_db/destroy_db to make params instead of + explicit values + + * Makefile.ov (TOP): Use ../../kadmin, not kadmin.ov + + diff --git a/src/lib/kadm5/Makefile.in b/src/lib/kadm5/Makefile.in new file mode 100644 index 000000000..ef500d3f2 --- /dev/null +++ b/src/lib/kadm5/Makefile.in @@ -0,0 +1,165 @@ +CFLAGS = $(CCOPTS) $(DEFS) + +##DOSBUILDTOP = ..\.. +##DOSLIBNAME=libkadm5srv.lib + +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + +kadm_err.$(OBJEXT): kadm_err.c + $(CC) $(CFLAGS) -c $*.c +@SHARED_RULE_LOCAL@ + +adb_err.$(OBJEXT): adb_err.c + $(CC) $(CFLAGS) -c $*.c +@SHARED_RULE_LOCAL@ + +chpass_util_strings.$(OBJEXT): chpass_util_strings.c + $(CC) $(CFLAGS) -c $*.c +@SHARED_RULE_LOCAL@ + +kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et +adb_err.c adb_err.h: $(srcdir)/adb_err.et +chpass_util_strings.c chpass_util_strings.h: $(srcdir)/chpass_util_strings.et + +clean:: + $(RM) kadm_err.c kadm_err.h kadm_err.o + $(RM) adb_err.c adb_err.h adb_err.o + $(RM) chpass_util_strings.c chpass_util_strings.h chpass_util_strings.o + +GENSRCS = kadm_err.c \ + adb_err.c \ + chpass_util_strings.c \ + $(srcdir)/ovsec_glue.c \ + $(srcdir)/misc_free.c \ + $(srcdir)/kadm_rpc_xdr.c \ + $(srcdir)/chpass_util.c \ + $(srcdir)/alt_prof.c \ + $(srcdir)/str_conv.c \ + $(srcdir)/logger.c \ + +SRVSRCS = $(GENSRCS) \ + $(srcdir)/svr_policy.c \ + $(srcdir)/svr_principal.c \ + $(srcdir)/server_acl.c \ + $(srcdir)/server_kdb.c \ + $(srcdir)/server_misc.c \ + $(srcdir)/server_init.c \ + $(srcdir)/server_dict.c \ + $(srcdir)/svr_iters.c \ + $(srcdir)/svr_chpass_util.c \ + $(srcdir)/adb_xdr.c \ + $(srcdir)/adb_policy.c \ + $(srcdir)/adb_free.c \ + $(srcdir)/adb_openclose.c + +CLNTSRCS = $(GENSRCS) \ + $(srcdir)/clnt_policy.c \ + $(srcdir)/client_rpc.c \ + $(srcdir)/client_principal.c \ + $(srcdir)/client_init.c \ + $(srcdir)/clnt_privs.c \ + $(srcdir)/clnt_chpass_util.c + +GENOBJS = kadm_err.$(OBJEXT) \ + adb_err.$(OBJEXT) \ + chpass_util_strings.$(OBJEXT) \ + ovsec_glue.$(OBJEXT) \ + misc_free.$(OBJEXT) \ + kadm_rpc_xdr.$(OBJEXT) \ + chpass_util.$(OBJEXT) \ + alt_prof.$(OBJEXT) \ + str_conv.$(OBJEXT) \ + logger.$(OBJEXT) \ + +SRVOBJS = $(GENOBJS) \ + svr_policy.$(OBJEXT) \ + svr_principal.$(OBJEXT) \ + server_acl.$(OBJEXT) \ + server_kdb.$(OBJEXT) \ + server_misc.$(OBJEXT) \ + server_init.$(OBJEXT) \ + server_dict.$(OBJEXT) \ + svr_iters.$(OBJEXT) \ + svr_chpass_util.$(OBJEXT) \ + adb_xdr.$(OBJEXT) \ + adb_policy.$(OBJEXT) \ + adb_free.$(OBJEXT) \ + adb_openclose.$(OBJEXT) + +CLNTOBJS = $(GENOBJS) @LIBOBJS@ \ + clnt_policy.$(OBJEXT) \ + client_rpc.$(OBJEXT) \ + client_principal.$(OBJEXT) \ + client_init.$(OBJEXT) \ + clnt_privs.$(OBJEXT) \ + clnt_chpass_util.$(OBJEXT) + +# +# Depends on libkdb5, libkrb5, libcrypto, libcom_err, libdyn +# +KDB5_VER=@KDB5_SH_VERS@ +KRB5_VER=@KRB5_SH_VERS@ +CRYPTO_VER=@CRYPTO_SH_VERS@ +COMERR_VER=@COMERR_SH_VERS@ +DYN_VER=@DYN_SH_VERS@ +DEPLIBS=$(TOPLIBD)/libkdb5.$(SHEXT).$(KDB5_VER) \ + $(TOPLIBD)/libkrb5.$(SHEXT).$(KRB5_VER) \ + $(TOPLIBD)/libcrypto.$(SHEXT).$(CRYPTO_VER) \ + $(TOPLIBD)/libcom_err.$(SHEXT).$(COMERR_VER) \ + $(TOPLIBD)/libdyn.$(SHEXT).$(DYN_VER) + +SHLIB_LIBS=-lkdb5 -lkrb5 -lcrypto -lcom_err -ldyn +SHLIB_LDFLAGS= $(LDFLAGS) @SHLIB_RPATH_DIRS@ +SHLIB_LIBDIRS= @SHLIB_LIBDIRS@ + +all-unix:: shared includes $(OBJS) +all-mac:: $(OBJS) +all-windows:: $(OBJS) + +# don't think about this very hard. when the build system goes away, +# so will this. +LIBDONE = srv/DONE clnt/DONE +LIB_SUBDIRS= +shared: + -mkdir shared srv clnt + ln -s ../shared srv/shared + ln -s ../shared clnt/shared + +srv/DONE: $(SRVOBJS) + $(RM) srv/DONE + echo $(SRVOBJS) > srv/DONE + +clnt/DONE: $(CLNTOBJS) + $(RM) clnt/DONE + echo $(CLNTOBJS) > clnt/DONE + +check-windows:: + +clean-unix:: + $(RM) shared/* srv/* clnt/* + -rmdir shared srv clnt + +clean-mac:: +clean-windows:: + +libkadm5srv.a: $(SRVOBJS) + $(RM) $@ + $(ARADD) $@ $(SRVOBJS) + $(RANLIB) $@ + +libkadm5clnt.a: $(CLNTOBJS) + $(RM) $@ + $(ARADD) $@ $(CLNTOBJS) + $(RANLIB) $@ + +install:: libkadm5srv.a libkadm5clnt.a + $(INSTALL_DATA) libkadm5srv.a $(DESTDIR)$(KRB5_LIBDIR)/libkadm5srv.a + $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkadm5srv.a + $(INSTALL_DATA) libkadm5clnt.a $(DESTDIR)$(KRB5_LIBDIR)/libkadm5clnt.a + $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkadm5clnt.a + +clean:: + $(RM) libkadm5srv.a libkadm5srv.bak DONESRV + $(RM) libkadm5clnt.a libkadm5clnt.bak DONECLNT diff --git a/src/lib/kadm5/Makefile.ov b/src/lib/kadm5/Makefile.ov new file mode 100644 index 000000000..6b078f777 --- /dev/null +++ b/src/lib/kadm5/Makefile.ov @@ -0,0 +1,61 @@ +TOP = ../../kadmin +include $(TOP)/config.mk/template + +# All but D_REGEXP_TYPE are needed only for logger.c +CFLAGS += $(D_REGEXP_TYPE) $(D_HAVE_SYSLOG_H) $(D_HAVE_STDARG_H) \ + $(D_HAVE_SYSLOG) $(D_HAVE_VSPRINTF) $(D_HAVE_OPENLOG) \ + $(D_HAVE_CLOSELOG) $(D_HAVE_STRFTIME) + +ifdef D_NO_SETENV +SETENVC = setenv.c +SETENVO = setenv.o +endif + +SUBDIRS = unit-test + +COMMON_SRCS := kadm_err.c adb_err.c chpass_util_strings.c ovsec_glue.c \ + misc_free.c kadm_rpc_xdr.c chpass_util.c alt_prof.c str_conv.c \ + logger.c $(SETENVC) +COMMON_OBJS := kadm_err.o adb_err.o chpass_util_strings.o ovsec_glue.o \ + misc_free.o kadm_rpc_xdr.o chpass_util.o alt_prof.o str_conv.o \ + logger.o $(SETENVO) + +SERVER_SRCS := svr_policy.c svr_principal.c server_kdb.c server_misc.c \ + server_init.c server_dict.c server_acl.c svr_iters.c \ + svr_chpass_util.c +SERVER_OBJS := svr_policy.o svr_principal.o server_kdb.o server_misc.o \ + server_init.o server_dict.o server_acl.o svr_iters.o \ + svr_chpass_util.o +DB_SRCS := adb_xdr.c adb_policy.c adb_free.c adb_openclose.c +DB_OBJS := adb_xdr.o adb_policy.o adb_free.o adb_openclose.o + +CLIENT_SRCS := clnt_policy.c client_rpc.c client_principal.c \ + client_init.c clnt_privs.c clnt_chpass_util.c +CLIENT_OBJS := clnt_policy.o client_rpc.o client_principal.o \ + client_init.o clnt_privs.o clnt_chpass_util.o + +HDRS := kadm_rpc.h admin.h admin_xdr.h adb.h admin_internal.h \ + server_internal.h server_acl.h +HDRS_DIR := kadm5 +ETABLES := chpass_util_strings.et kadm_err.et adb_err.et + +SRCS := $(COMMON_SRCS) $(SERVER_SRCS) $(DB_SRCS) +OBJS := $(COMMON_OBJS) $(SERVER_OBJS) $(DB_OBJS) +LIB := libkadm5srv.a + +expand StageLibrary +expand Depend + +SRCS = $(COMMON_SRCS) $(CLIENT_SRCS) +OBJS = $(COMMON_OBJS) $(CLIENT_OBJS) +LIB = libkadm5clnt.a + +expand StageLibrary +expand Depend + +expand SubdirTarget +expand StageIncludes +expand StageErrorTables + +# Not sure if/why this is needed... +chpass_util.c: chpass_util_strings.h diff --git a/src/lib/kadm5/adb.h b/src/lib/kadm5/adb.h new file mode 100644 index 000000000..b73553575 --- /dev/null +++ b/src/lib/kadm5/adb.h @@ -0,0 +1,141 @@ +/* + * Data Types for policys, and principal information that + * exist in the respective databases. + * + * $Header$ + * + * This file was originally created with rpcgen. + * It has been hacked up since then. + */ + +#ifndef __ADB_H__ +#define __ADB_H__ +#include +#include +#include "k5-int.h" +#include +#include +#include +#include +#include + +typedef long osa_adb_ret_t; + +#define OSA_ADB_POLICY_DB_MAGIC 0x12345A00 +#define OSA_ADB_PRINC_DB_MAGIC 0x12345B00 + +#define OSA_ADB_SHARED 0x7001 +#define OSA_ADB_EXCLUSIVE 0x7002 +#define OSA_ADB_PERMANENT 0x7003 + +#define OSA_ADB_PRINC_VERSION_MASK 0x12345C00 +#define OSA_ADB_PRINC_VERSION_1 0x12345C01 +#define OSA_ADB_POLICY_VERSION_MASK 0x12345D00 +#define OSA_ADB_POLICY_VERSION_1 0x12345D01 + +typedef struct _osa_adb_db_lock_ent_t { + FILE *lockfile; + char *filename; + int refcnt, lockmode, lockcnt; + krb5_context context; +} osa_adb_lock_ent, *osa_adb_lock_t; + +typedef struct _osa_adb_db_ent_t { + int magic; + DB *db; + HASHINFO info; + char *filename; + osa_adb_lock_t lock; +} osa_adb_db_ent, *osa_adb_db_t, *osa_adb_princ_t, *osa_adb_policy_t; + +/* an osa_pw_hist_ent stores all the key_datas for a single password */ +typedef struct _osa_pw_hist_t { + int n_key_data; + krb5_key_data *key_data; +} osa_pw_hist_ent, *osa_pw_hist_t; + +typedef struct _osa_princ_ent_t { + int version; + char *policy; + long aux_attributes; + unsigned int old_key_len; + unsigned int old_key_next; + krb5_kvno admin_history_kvno; + osa_pw_hist_ent *old_keys; +} osa_princ_ent_rec, *osa_princ_ent_t; + +typedef struct _osa_policy_ent_t { + int version; + char *name; + rpc_u_int32 pw_min_life; + rpc_u_int32 pw_max_life; + rpc_u_int32 pw_min_length; + rpc_u_int32 pw_min_classes; + rpc_u_int32 pw_history_num; + rpc_u_int32 policy_refcnt; +} osa_policy_ent_rec, *osa_policy_ent_t; + +typedef void (*osa_adb_iter_princ_func) (void *, osa_princ_ent_t); +typedef void (*osa_adb_iter_policy_func) (void *, osa_policy_ent_t); + + +/* + * Return Code (the rest are in adb_err.h) + */ + +#define OSA_ADB_OK 0 + +/* + * xdr functions + */ +bool_t xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp); +bool_t xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp); + +/* + * Functions + */ + +osa_adb_ret_t osa_adb_create_db(char *filename, char *lockfile, int magic); +osa_adb_ret_t osa_adb_destroy_db(char *filename, char *lockfile, int magic); +osa_adb_ret_t osa_adb_init_db(osa_adb_db_t *dbp, char *filename, + char *lockfile, int magic); +osa_adb_ret_t osa_adb_fini_db(osa_adb_db_t db, int magic); +osa_adb_ret_t osa_adb_get_lock(osa_adb_db_t db, int mode); +osa_adb_ret_t osa_adb_release_lock(osa_adb_db_t db); +osa_adb_ret_t osa_adb_open_and_lock(osa_adb_princ_t db, int locktype); +osa_adb_ret_t osa_adb_close_and_unlock(osa_adb_princ_t db); + +osa_adb_ret_t osa_adb_create_policy_db(kadm5_config_params *params); +osa_adb_ret_t osa_adb_destroy_policy_db(kadm5_config_params *params); +osa_adb_ret_t osa_adb_open_princ(osa_adb_princ_t *db, char *filename); +osa_adb_ret_t osa_adb_open_policy(osa_adb_policy_t *db, + kadm5_config_params *rparams); +osa_adb_ret_t osa_adb_close_princ(osa_adb_princ_t db); +osa_adb_ret_t osa_adb_close_policy(osa_adb_policy_t db); +osa_adb_ret_t osa_adb_create_princ(osa_adb_princ_t db, + osa_princ_ent_t entry); +osa_adb_ret_t osa_adb_create_policy(osa_adb_policy_t db, + osa_policy_ent_t entry); +osa_adb_ret_t osa_adb_destroy_princ(osa_adb_princ_t db, + kadm5_princ_t name); +osa_adb_ret_t osa_adb_destroy_policy(osa_adb_policy_t db, + kadm5_policy_t name); +osa_adb_ret_t osa_adb_get_princ(osa_adb_princ_t db, + kadm5_princ_t name, + osa_princ_ent_t *entry); +osa_adb_ret_t osa_adb_get_policy(osa_adb_policy_t db, + kadm5_policy_t name, + osa_policy_ent_t *entry); +osa_adb_ret_t osa_adb_put_princ(osa_adb_princ_t db, + osa_princ_ent_t entry); +osa_adb_ret_t osa_adb_put_policy(osa_adb_policy_t db, + osa_policy_ent_t entry); +osa_adb_ret_t osa_adb_iter_policy(osa_adb_policy_t db, + osa_adb_iter_policy_func func, + void * data); +osa_adb_ret_t osa_adb_iter_princ(osa_adb_princ_t db, + osa_adb_iter_princ_func func, + void *data); +void osa_free_policy_ent(osa_policy_ent_t val); +void osa_free_princ_ent(osa_princ_ent_t val); +#endif /* __ADB_H__ */ diff --git a/src/lib/kadm5/adb_err.et b/src/lib/kadm5/adb_err.et new file mode 100644 index 000000000..394802571 --- /dev/null +++ b/src/lib/kadm5/adb_err.et @@ -0,0 +1,16 @@ +error_table adb +error_code OSA_ADB_NOERR, "No Error" +error_code OSA_ADB_DUP, "Principal or policy already exists" +error_code OSA_ADB_NOENT, "Principal or policy does not exist" +error_code OSA_ADB_DBINIT, "Database not initialized" +error_code OSA_ADB_BAD_POLICY, "Invalid policy name" +error_code OSA_ADB_BAD_PRINC, "Invalid principal name" +error_code OSA_ADB_BAD_DB, "Database inconsistency detected" +error_code OSA_ADB_XDR_FAILURE, "XDR encoding error" +error_code OSA_ADB_FAILURE, "Failure!" +error_code OSA_ADB_BADLOCKMODE, "Bad lock mode" +error_code OSA_ADB_CANTLOCK_DB, "Cannot lock database" +error_code OSA_ADB_NOTLOCKED, "Database not locked" +error_code OSA_ADB_NOLOCKFILE, "KADM5 administration database lock file missing" +error_code OSA_ADB_NOEXCL_PERM, "Insufficient permission to lock file" +end diff --git a/src/lib/kadm5/adb_free.c b/src/lib/kadm5/adb_free.c new file mode 100644 index 000000000..4c6f8a66d --- /dev/null +++ b/src/lib/kadm5/adb_free.c @@ -0,0 +1,71 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.8 1996/07/22 20:35:16 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.7.4.1 1996/07/18 03:08:07 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.7.2.1 1996/06/20 02:16:25 marc + * File added to the repository on a branch + * + * Revision 1.7 1996/05/12 06:21:57 marc + * don't use for "internal header files" + * + * Revision 1.6 1993/12/13 21:15:56 shanzer + * fixed memory leak + * ., + * + * Revision 1.5 1993/12/06 22:20:37 marc + * fixup free functions to use xdr to free the underlying struct + * + * Revision 1.4 1993/11/15 00:29:46 shanzer + * check to make sure pointers are somewhat vaid before freeing. + * + * Revision 1.3 1993/11/09 04:02:24 shanzer + * added some includefiles + * changed bzero to memset + * + * Revision 1.2 1993/11/04 01:54:24 shanzer + * added rcs header .. + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "adb.h" +#include +#include + +void +osa_free_princ_ent(osa_princ_ent_t val) +{ + XDR xdrs; + + xdrmem_create(&xdrs, NULL, 0, XDR_FREE); + + xdr_osa_princ_ent_rec(&xdrs, val); + free(val); +} + +void +osa_free_policy_ent(osa_policy_ent_t val) +{ + XDR xdrs; + + xdrmem_create(&xdrs, NULL, 0, XDR_FREE); + + xdr_osa_policy_ent_rec(&xdrs, val); + free(val); +} + diff --git a/src/lib/kadm5/adb_openclose.c b/src/lib/kadm5/adb_openclose.c new file mode 100644 index 000000000..627a6b410 --- /dev/null +++ b/src/lib/kadm5/adb_openclose.c @@ -0,0 +1,338 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "adb.h" +#include + +#define MAX_LOCK_TRIES 5 + +struct _locklist { + osa_adb_lock_ent lockinfo; + struct _locklist *next; +}; + +osa_adb_ret_t osa_adb_create_db(char *filename, char *lockfilename, + int magic) +{ + FILE *lf; + DB *db; + HASHINFO info; + + lf = fopen(lockfilename, "w+"); + if (lf == NULL) + return errno; + (void) fclose(lf); + + memset(&info, 0, sizeof(info)); + info.hash = NULL; + info.bsize = 256; + info.ffactor = 8; + info.nelem = 25000; + info.lorder = 0; + db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_HASH, &info); + if (db == NULL) + return errno; + if (db->close(db) < 0) + return errno; + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_destroy_db(char *filename, char *lockfilename, + int magic) +{ + /* the admin databases do not contain security-critical data */ + if (unlink(filename) < 0 || + unlink(lockfilename) < 0) + return errno; + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_init_db(osa_adb_db_t *dbp, char *filename, + char *lockfilename, int magic) +{ + osa_adb_db_t db; + static struct _locklist *locklist = NULL; + struct _locklist *lockp; + krb5_error_code code; + + if (dbp == NULL || filename == NULL) + return EINVAL; + + db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent)); + if (db == NULL) + return ENOMEM; + + memset(db, 0, sizeof(*db)); + db->info.hash = NULL; + db->info.bsize = 256; + db->info.ffactor = 8; + db->info.nelem = 25000; + db->info.lorder = 0; + + /* + * A process is allowed to open the same database multiple times + * and access it via different handles. If the handles use + * distinct lockinfo structures, things get confused: lock(A), + * lock(B), release(B) will result in the kernel unlocking the + * lock file but handle A will still think the file is locked. + * Therefore, all handles using the same lock file must share a + * single lockinfo structure. + * + * It is not sufficient to have a single lockinfo structure, + * however, because a single process may also wish to open + * multiple different databases simultaneously, with different + * lock files. This code used to use a single static lockinfo + * structure, which means that the second database opened used + * the first database's lock file. This was Bad. + * + * We now maintain a linked list of lockinfo structures, keyed by + * lockfilename. An entry is added when this function is called + * with a new lockfilename, and all subsequent calls with that + * lockfilename use the existing entry, updating the refcnt. + * When the database is closed with fini_db(), the refcnt is + * decremented, and when it is zero the lockinfo structure is + * freed and reset. The entry in the linked list, however, is + * never removed; it will just be reinitialized the next time + * init_db is called with the right lockfilename. + */ + + /* find or create the lockinfo structure for lockfilename */ + lockp = locklist; + while (lockp) { + if (strcmp(lockp->lockinfo.filename, lockfilename) == 0) + break; + else + lockp = lockp->next; + } + if (lockp == NULL) { + /* doesn't exist, create it, add to list */ + lockp = (struct _locklist *) malloc(sizeof(*lockp)); + if (lockp == NULL) { + free(db); + return ENOMEM; + } + memset(lockp, 0, sizeof(*lockp)); + lockp->next = locklist; + locklist = lockp; + } + + /* now initialize lockp->lockinfo if necessary */ + if (lockp->lockinfo.lockfile == NULL) { + if (code = krb5_init_context(&lockp->lockinfo.context)) { + free(db); + return((osa_adb_ret_t) code); + } + + /* + * needs be open read/write so that write locking can work with + * POSIX systems + */ + lockp->lockinfo.filename = strdup(lockfilename); + if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) { + /* + * maybe someone took away write permission so we could only + * get shared locks? + */ + if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r")) + == NULL) { + free(db); + return OSA_ADB_NOLOCKFILE; + } + } + lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0; + } + + /* lockp is set, lockinfo is initialized, update the reference count */ + db->lock = &lockp->lockinfo; + db->lock->refcnt++; + + db->filename = strdup(filename); + db->magic = magic; + + *dbp = db; + + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_fini_db(osa_adb_db_t db, int magic) +{ + if (db->magic != magic) + return EINVAL; + if (db->lock->refcnt == 0) { + /* barry says this can't happen */ + return OSA_ADB_FAILURE; + } else { + db->lock->refcnt--; + } + + if (db->lock->refcnt == 0) { + /* + * Don't free db->lock->filename, it is used as a key to + * find the lockinfo entry in the linked list. If the + * lockfile doesn't exist, we must be closing the database + * after trashing it. This has to be allowed, so don't + * generate an error. + */ + (void) fclose(db->lock->lockfile); + db->lock->lockfile = NULL; + krb5_free_context(db->lock->context); + } + + db->magic = 0; + free(db->filename); + free(db); + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_get_lock(osa_adb_db_t db, int mode) +{ + int tries, gotlock, perm, krb5_mode, ret; + + if (db->lock->lockmode >= mode) { + /* No need to upgrade lock, just incr refcnt and return */ + db->lock->lockcnt++; + return(OSA_ADB_OK); + } + + perm = 0; + switch (mode) { + case OSA_ADB_PERMANENT: + perm = 1; + case OSA_ADB_EXCLUSIVE: + krb5_mode = KRB5_LOCKMODE_EXCLUSIVE; + break; + case OSA_ADB_SHARED: + krb5_mode = KRB5_LOCKMODE_SHARED; + break; + default: + return(EINVAL); + } + + for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { + if ((ret = krb5_lock_file(db->lock->context, + fileno(db->lock->lockfile), + krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) { + gotlock++; + break; + } else if (ret == EBADF && mode == OSA_ADB_EXCLUSIVE) + /* tried to exclusive-lock something we don't have */ + /* write access to */ + return OSA_ADB_NOEXCL_PERM; + + sleep(1); + } + + /* test for all the likely "can't get lock" error codes */ + if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK) + return OSA_ADB_CANTLOCK_DB; + else if (ret != 0) + return ret; + + /* + * If the file no longer exists, someone acquired a permanent + * lock. If that process terminates its exclusive lock is lost, + * but if we already had the file open we can (probably) lock it + * even though it has been unlinked. So we need to insist that + * it exist. + */ + if (access(db->lock->filename, F_OK) < 0) { + (void) krb5_lock_file(db->lock->context, + fileno(db->lock->lockfile), + KRB5_LOCKMODE_UNLOCK); + return OSA_ADB_NOLOCKFILE; + } + + /* we have the shared/exclusive lock */ + + if (perm) { + if (unlink(db->lock->filename) < 0) { + int ret; + + /* somehow we can't delete the file, but we already */ + /* have the lock, so release it and return */ + + ret = errno; + (void) krb5_lock_file(db->lock->context, + fileno(db->lock->lockfile), + KRB5_LOCKMODE_UNLOCK); + + /* maybe we should return CANTLOCK_DB.. but that would */ + /* look just like the db was already locked */ + return ret; + } + + /* this releases our exclusive lock.. which is okay because */ + /* now no one else can get one either */ + (void) fclose(db->lock->lockfile); + } + + db->lock->lockmode = mode; + db->lock->lockcnt++; + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_release_lock(osa_adb_db_t db) +{ + int ret; + + if (!db->lock->lockcnt) /* lock already unlocked */ + return OSA_ADB_NOTLOCKED; + + if (--db->lock->lockcnt == 0) { + if (db->lock->lockmode == OSA_ADB_PERMANENT) { + /* now we need to create the file since it does not exist */ + if ((db->lock->lockfile = fopen(db->lock->filename, + "w+")) == NULL) + return OSA_ADB_NOLOCKFILE; + } else if (ret = krb5_lock_file(db->lock->context, + fileno(db->lock->lockfile), + KRB5_LOCKMODE_UNLOCK)) + return ret; + + db->lock->lockmode = 0; + } + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_open_and_lock(osa_adb_princ_t db, int locktype) +{ + int ret; + + ret = osa_adb_get_lock(db, locktype); + if (ret != OSA_ADB_OK) + return ret; + + db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info); + if (db->db == NULL) { + (void) osa_adb_release_lock(db); + if(errno == EINVAL) + return OSA_ADB_BAD_DB; + return errno; + } + return OSA_ADB_OK; +} + +osa_adb_ret_t osa_adb_close_and_unlock(osa_adb_princ_t db) +{ + int ret; + + if(db->db->close(db->db) == -1) { + (void) osa_adb_release_lock(db); + return OSA_ADB_FAILURE; + } + + db->db = NULL; + + return(osa_adb_release_lock(db)); +} + diff --git a/src/lib/kadm5/adb_policy.c b/src/lib/kadm5/adb_policy.c new file mode 100644 index 000000000..ff0117bac --- /dev/null +++ b/src/lib/kadm5/adb_policy.c @@ -0,0 +1,401 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include "adb.h" +#include +#include +#include + +extern int errno; + +#define OPENLOCK(db, mode) \ +{ \ + int ret; \ + if (db == NULL) \ + return EINVAL; \ + else if (db->magic != OSA_ADB_POLICY_DB_MAGIC) \ + return OSA_ADB_DBINIT; \ + else if ((ret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \ + return ret; \ + } + +#define CLOSELOCK(db) \ +{ \ + int ret; \ + if ((ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \ + return ret; \ +} + +osa_adb_ret_t osa_adb_create_policy_db(kadm5_config_params *params) +{ + return osa_adb_create_db(params->admin_dbname, + params->admin_lockfile, + OSA_ADB_POLICY_DB_MAGIC); +} + +osa_adb_ret_t osa_adb_destroy_policy_db(kadm5_config_params *params) +{ + return osa_adb_destroy_db(params->admin_dbname, + params->admin_lockfile, + OSA_ADB_POLICY_DB_MAGIC); +} + +osa_adb_ret_t osa_adb_open_policy(osa_adb_princ_t *dbp, + kadm5_config_params *rparams) +{ + return osa_adb_init_db(dbp, rparams->admin_dbname, + rparams->admin_lockfile, + OSA_ADB_POLICY_DB_MAGIC); +} + +osa_adb_ret_t osa_adb_close_policy(osa_adb_princ_t db) +{ + return osa_adb_fini_db(db, OSA_ADB_POLICY_DB_MAGIC); +} + +/* + * Function: osa_adb_create_policy + * + * Purpose: create a policy entry in the policy db. + * + * Arguments: + * entry (input) pointer to the entry to be added + * OSA_ADB_OK on sucsess, else error code. + * + * Requires: + * entry have a valid name. + * + * Effects: + * creates the entry in the db + * + * Modifies: + * the policy db. + * + */ +osa_adb_ret_t +osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry) +{ + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(entry->name == NULL) { + ret = EINVAL; + goto error; + } + dbkey.data = entry->name; + dbkey.size = (strlen(entry->name) + 1); + + switch(db->db->get(db->db, &dbkey, &dbdata, 0)) { + case 0: + ret = OSA_ADB_DUP; + goto error; + case 1: + break; + default: + ret = errno; + goto error; + } + xdralloc_create(&xdrs, XDR_ENCODE); + if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto error; + } + dbdata.data = xdralloc_getdata(&xdrs); + dbdata.size = xdr_getpos(&xdrs); + switch(db->db->put(db->db, &dbkey, &dbdata, R_NOOVERWRITE)) { + case 0: + if((db->db->sync(db->db, 0)) == -1) + ret = OSA_ADB_FAILURE; + ret = OSA_ADB_OK; + break; + case 1: + ret = OSA_ADB_DUP; + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + xdr_destroy(&xdrs); + +error: + CLOSELOCK(db); + return ret; +} + +/* + * Function: osa_adb_destroy_policy + * + * Purpose: destroy a policy entry + * + * Arguments: + * db (input) database handle + * name (input) name of policy + * OSA_ADB_OK on sucsess, or error code. + * + * Requires: + * db being valid. + * name being non-null. + * Effects: + * deletes policy from db. + * + * Modifies: + * policy db. + * + */ +osa_adb_ret_t +osa_adb_destroy_policy(osa_adb_policy_t db, kadm5_policy_t name) +{ + DBT dbkey; + int status, ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(name == NULL) { + ret = EINVAL; + goto error; + } + dbkey.data = name; + dbkey.size = (strlen(name) + 1); + + status = db->db->del(db->db, &dbkey, 0); + switch(status) { + case 1: + ret = OSA_ADB_NOENT; + goto error; + case 0: + if ((db->db->sync(db->db, 0)) == -1) { + ret = OSA_ADB_FAILURE; + goto error; + } + ret = OSA_ADB_OK; + break; + default: + ret = OSA_ADB_FAILURE; + goto error; + } + +error: + CLOSELOCK(db); + return ret; +} + +/* + * Function: osa_adb_get_policy + * + * Purpose: retrieve policy + * + * Arguments: + * db (input) db handle + * name (input) name of policy + * entry (output) policy entry + * 0 on sucsess, error code on failure. + * + * Requires: + * Effects: + * Modifies: + */ +osa_adb_ret_t +osa_adb_get_policy(osa_adb_policy_t db, kadm5_policy_t name, + osa_policy_ent_t *entry) +{ + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret; + char *aligned_data; + + OPENLOCK(db, OSA_ADB_SHARED); + + if(name == NULL) { + ret = EINVAL; + goto error; + } + dbkey.data = name; + dbkey.size = (strlen(dbkey.data) + 1); + dbdata.data = NULL; + dbdata.size = 0; + switch((db->db->get(db->db, &dbkey, &dbdata, 0))) { + case 1: + ret = OSA_ADB_NOENT; + goto error; + case 0: + break; + default: + ret = OSA_ADB_FAILURE; + goto error; + } + if (!(*(entry) = (osa_policy_ent_t)malloc(sizeof(osa_policy_ent_rec)))) { + ret = ENOMEM; + goto error; + } + if (!(aligned_data = (char *) malloc(dbdata.size))) { + ret = ENOMEM; + goto error; + } + memcpy(aligned_data, dbdata.data, dbdata.size); + memset(*entry, 0, sizeof(osa_policy_ent_rec)); + xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); + if (!xdr_osa_policy_ent_rec(&xdrs, *entry)) + ret = OSA_ADB_FAILURE; + else ret = OSA_ADB_OK; + xdr_destroy(&xdrs); + free(aligned_data); + +error: + CLOSELOCK(db); + return ret; +} + +/* + * Function: osa_adb_put_policy + * + * Purpose: update a policy in the dababase + * + * Arguments: + * db (input) db handle + * entry (input) policy entry + * 0 on sucsess error code on failure. + * + * Requires: + * [requires] + * + * Effects: + * [effects] + * + * Modifies: + * [modifies] + * + */ +osa_adb_ret_t +osa_adb_put_policy(osa_adb_policy_t db, osa_policy_ent_t entry) +{ + DBT dbkey; + DBT dbdata; + DBT tmpdb; + XDR xdrs; + int ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(entry->name == NULL) { + ret = EINVAL; + goto error; + } + dbkey.data = entry->name; + dbkey.size = (strlen(entry->name) + 1); + switch(db->db->get(db->db, &dbkey, &tmpdb, 0)) { + case 0: + break; + case 1: + ret = OSA_ADB_NOENT; + goto error; + default: + ret = OSA_ADB_FAILURE; + goto error; + } + xdralloc_create(&xdrs, XDR_ENCODE); + if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto error; + } + dbdata.data = xdralloc_getdata(&xdrs); + dbdata.size = xdr_getpos(&xdrs); + switch(db->db->put(db->db, &dbkey, &dbdata, 0)) { + case 0: + if((db->db->sync(db->db, 0)) == -1) + ret = OSA_ADB_FAILURE; + ret = OSA_ADB_OK; + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + xdr_destroy(&xdrs); + +error: + CLOSELOCK(db); + return ret; +} + +/* + * Function: osa_adb_iter_policy + * + * Purpose: iterate over the policy database. + * + * Arguments: + * db (input) db handle + * func (input) fucntion pointer to call + * data opaque data type + * 0 on sucsess error code on failure + * + * Requires: + * Effects: + * Modifies: + */ +osa_adb_ret_t +osa_adb_iter_policy(osa_adb_policy_t db, osa_adb_iter_policy_func func, + void *data) +{ + DBT dbkey, + dbdata; + XDR xdrs; + int ret; + osa_policy_ent_t entry; + char *aligned_data; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); /* hmmm */ + + if((ret = db->db->seq(db->db, &dbkey, &dbdata, R_FIRST)) == -1) { + ret = errno; + goto error; + } + + while (ret == 0) { + if (!(entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec)))) { + ret = ENOMEM; + goto error; + } + + if(!(aligned_data = (char *) malloc(dbdata.size))) { + ret = ENOMEM; + goto error; + } + memcpy(aligned_data, dbdata.data, dbdata.size); + + memset(entry, 0, sizeof(osa_policy_ent_rec)); + xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); + if(!xdr_osa_policy_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + free(aligned_data); + ret = OSA_ADB_FAILURE; + goto error; + } + (*func)(data, entry); + xdr_destroy(&xdrs); + free(aligned_data); + osa_free_policy_ent(entry); + ret = db->db->seq(db->db, &dbkey, &dbdata, R_NEXT); + } + if(ret == -1) + ret = errno; + else ret = OSA_ADB_OK; + +error: + CLOSELOCK(db); + return ret; +} diff --git a/src/lib/kadm5/adb_principal.c b/src/lib/kadm5/adb_principal.c new file mode 100644 index 000000000..8ee9aab30 --- /dev/null +++ b/src/lib/kadm5/adb_principal.c @@ -0,0 +1,408 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.24 1996/07/22 20:35:23 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.23.4.1 1996/07/18 03:08:17 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.23.2.1 1996/06/20 02:16:30 marc + * File added to the repository on a branch + * + * Revision 1.23 1996/05/16 21:44:35 bjaspan + * this file is no longer used, #if the whole thing out + * + * Revision 1.22 1996/05/08 20:51:44 bjaspan + * marc's changes + * + * Revision 1.21 1995/08/24 20:23:43 bjaspan + * marc is a bonehead + * + * Revision 1.20 1995/08/23 19:16:02 marc + * check for db == NULL in OPENLOCK() + * + * Revision 1.19 1995/08/08 18:31:30 bjaspan + * [secure/3394] first cut at admin db locking support + * + * Revision 1.18 1995/08/02 15:26:57 bjaspan + * check db==NULL in iter + * + * Revision 1.17 1994/05/09 17:52:36 shanzer + * fixed some include files + * + * Revision 1.16 1994/03/17 01:25:58 shanzer + * include fcntl.h + * + * Revision 1.15 1993/12/17 18:54:06 jik + * [secure-admin/1040] + * + * open_princ should return errno, rather than BAD_DB, if errno is + * something other than BAD_DB. + * + * Revision 1.14 1993/12/13 18:55:58 marc + * remove bogus free()'s + * + * Revision 1.13 1993/12/08 22:29:27 marc + * fixed another xdrmem alignment thing] + * + * Revision 1.12 1993/12/06 22:22:22 bjaspan + * fix alignment and free-memory-read bugs + * + * Revision 1.11 1993/12/05 04:15:16 shanzer + * removed data size hack. + * + * Revision 1.10 1993/11/15 00:29:24 shanzer + * added filenme to open + * + * Revision 1.9 1993/11/10 20:10:06 shanzer + * now uses xdralloc instead of xdrmem + * + * Revision 1.8 1993/11/09 21:43:24 shanzer + * added check to see if we overflowed our xdr buffer. + * + * Revision 1.7 1993/11/09 04:00:19 shanzer + * changed bzero to memset + * + * Revision 1.6 1993/11/05 23:16:21 shanzer + * return ENOMEM instead of ovsec_kadm_mem + * + * Revision 1.5 1993/11/05 22:17:03 shanzer + * added principal db interative function + * + * Revision 1.4 1993/11/04 23:20:24 shanzer + * made HASHINFO static. + * + * Revision 1.3 1993/11/04 01:52:30 shanzer + * Restructred some code .. fixed some bugs/leaks + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#if 0 +/* XXX THIS FILE IS NO LONGER USED, and should be deleted when we're done */ + +#include +#include +#include "adb.h" +#include +#include + +#define OPENLOCK(db, mode) \ +{ \ + int ret; \ + if (db == NULL) \ + return EINVAL; \ + else if (db->magic != OSA_ADB_PRINC_DB_MAGIC) \ + return OSA_ADB_DBINIT; \ + else if ((ret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \ + return ret; \ + } + +#define CLOSELOCK(db) \ +{ \ + int ret; \ + if ((ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \ + return ret; \ +} + +osa_adb_ret_t osa_adb_open_princ(osa_adb_princ_t *dbp, char *filename) +{ + return osa_adb_init_db(dbp, filename, OSA_ADB_PRINC_DB_MAGIC); +} + +osa_adb_ret_t osa_adb_close_princ(osa_adb_princ_t db) +{ + return osa_adb_fini_db(db, OSA_ADB_PRINC_DB_MAGIC); +} + +osa_adb_ret_t +osa_adb_create_princ(osa_adb_princ_t db, osa_princ_ent_t entry) +{ + + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(krb5_unparse_name(db->lock->context, + entry->name, (char **) &dbkey.data)) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + if((dbkey.size = strlen(dbkey.data)) == 0) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + + switch(db->db->get(db->db, &dbkey, &dbdata, 0)) { + case 0: + ret = OSA_ADB_DUP; + goto error; + case 1: + break; + default: + ret = OSA_ADB_FAILURE; + goto error; + } + xdralloc_create(&xdrs, XDR_ENCODE); + if(!xdr_osa_princ_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto error; + } + dbdata.data = xdralloc_getdata(&xdrs); + dbdata.size = xdr_getpos(&xdrs); + switch(db->db->put(db->db, &dbkey, &dbdata, R_NOOVERWRITE)) { + case 0: + if((db->db->sync(db->db, 0)) == -1) + ret = OSA_ADB_FAILURE; + else + ret = OSA_ADB_OK; + break; + case 1: + ret = OSA_ADB_DUP; + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + xdralloc_release(&xdrs); + free(dbkey.data); + +error: + CLOSELOCK(db); + + return ret; +} + +osa_adb_ret_t +osa_adb_destroy_princ(osa_adb_princ_t db, ovsec_kadm_princ_t name) +{ + DBT dbkey; + int status; + int ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(krb5_unparse_name(db->lock->context, name, (char **) &dbkey.data)) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + if ((dbkey.size = strlen(dbkey.data)) == 0) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + status = db->db->del(db->db, &dbkey, 0); + switch(status) { + case 1: + ret = OSA_ADB_NOENT; + break; + case 0: + if ((db->db->sync(db->db, 0)) == -1) + ret = OSA_ADB_FAILURE; + else + ret = OSA_ADB_OK; + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + free(dbkey.data); + +error: + CLOSELOCK(db); + + return ret; +} + +osa_adb_ret_t +osa_adb_get_princ(osa_adb_princ_t db, ovsec_kadm_princ_t name, + osa_princ_ent_t *entry) +{ + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret = 0; + char *aligned_data; + + OPENLOCK(db, OSA_ADB_SHARED); + + if(krb5_unparse_name(db->lock->context, name, (char **) &dbkey.data)) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + if((dbkey.size = strlen(dbkey.data)) == 0) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + dbdata.size = 0; + dbdata.data = NULL; + switch(db->db->get(db->db, &dbkey, &dbdata, 0)) { + case 1: + ret = OSA_ADB_NOENT; + break; + case 0: + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + free(dbkey.data); + if (ret) + goto error; + + if (!(*(entry) = (osa_princ_ent_t)malloc(sizeof(osa_princ_ent_rec)))) { + ret = ENOMEM; + goto error; + } + + aligned_data = (char *) malloc(dbdata.size); + if (aligned_data == NULL) { + ret = ENOMEM; + goto error; + } + memcpy(aligned_data, dbdata.data, dbdata.size); + + memset(*entry, 0, sizeof(osa_princ_ent_rec)); + xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); + if (!xdr_osa_princ_ent_rec(&xdrs, *entry)) { + xdr_destroy(&xdrs); + free(aligned_data); + ret = OSA_ADB_FAILURE; + goto error; + } + xdr_destroy(&xdrs); + free(aligned_data); + ret = OSA_ADB_OK; + +error: + CLOSELOCK(db); + return ret; +} + +osa_adb_ret_t +osa_adb_put_princ(osa_adb_princ_t db, osa_princ_ent_t entry) +{ + DBT dbkey; + DBT dbdata; + DBT tmpdb; + XDR xdrs; + int ret; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); + + if(krb5_unparse_name(db->lock->context, + entry->name, (char **) &dbkey.data)) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + if((dbkey.size = strlen(dbkey.data)) == 0) { + ret = OSA_ADB_BAD_PRINC; + goto error; + } + + switch(db->db->get(db->db, &dbkey, &tmpdb, 0)) { + case 0: + break; + case 1: + ret = OSA_ADB_NOENT; + goto error; + default: + ret = OSA_ADB_FAILURE; + goto error; + } + xdralloc_create(&xdrs, XDR_ENCODE); + if(!xdr_osa_princ_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto error; + } + dbdata.data = xdralloc_getdata(&xdrs); + dbdata.size = xdr_getpos(&xdrs); + switch(db->db->put(db->db, &dbkey, &dbdata, 0)) { + case 0: + if((db->db->sync(db->db, 0)) == -1) + ret = OSA_ADB_FAILURE; + else + ret = OSA_ADB_OK; + break; + default: + ret = OSA_ADB_FAILURE; + break; + } + xdralloc_release(&xdrs); + free(dbkey.data); + +error: + CLOSELOCK(db); + return ret; +} + +osa_adb_ret_t +osa_adb_iter_princ(osa_adb_princ_t db, osa_adb_iter_princ_func func, + void *data) +{ + DBT dbkey, + dbdata; + XDR xdrs; + int ret; + osa_princ_ent_t entry; + char *aligned_data; + + OPENLOCK(db, OSA_ADB_EXCLUSIVE); /* hmmmm */ + + if((ret = db->db->seq(db->db, &dbkey, &dbdata, R_FIRST)) == -1) { + ret = errno; + goto error; + } + while (ret == 0) { + if (!(entry = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec)))) { + ret = ENOMEM; + goto error; + } + + aligned_data = (char *) malloc(dbdata.size); + if (aligned_data == NULL) { + ret = ENOMEM; + goto error; + } + memcpy(aligned_data, dbdata.data, dbdata.size); + + memset(entry, 0, sizeof(osa_princ_ent_rec)); + xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE); + if(!xdr_osa_princ_ent_rec(&xdrs, entry)) { + xdr_destroy(&xdrs); + free(aligned_data); + ret = OSA_ADB_FAILURE; + goto error; + } + (*func)(data, entry); + xdr_destroy(&xdrs); + free(aligned_data); + osa_free_princ_ent(entry); + ret = db->db->seq(db->db, &dbkey, &dbdata, R_NEXT); + } + if(ret == -1) + ret = errno; + else + ret = OSA_ADB_OK; + +error: + CLOSELOCK(db); + return ret; +} + +#endif /* 0 */ diff --git a/src/lib/kadm5/adb_xdr.c b/src/lib/kadm5/adb_xdr.c new file mode 100644 index 000000000..944fb04b3 --- /dev/null +++ b/src/lib/kadm5/adb_xdr.c @@ -0,0 +1,132 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "adb.h" +#include "admin_xdr.h" +#include + +bool_t +xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp) +{ + unsigned int tmp; + + if (!xdr_krb5_int16(xdrs, &objp->key_data_ver)) + return(FALSE); + if (!xdr_krb5_int16(xdrs, &objp->key_data_kvno)) + return(FALSE); + if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) + return(FALSE); + if (!xdr_krb5_int16(xdrs, &objp->key_data_type[1])) + return(FALSE); + if (!xdr_krb5_int16(xdrs, &objp->key_data_length[0])) + return(FALSE); + if (!xdr_krb5_int16(xdrs, &objp->key_data_length[1])) + return(FALSE); + + tmp = (unsigned int) objp->key_data_length[0]; + if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[0], + &tmp, ~0)) + return FALSE; + + tmp = (unsigned int) objp->key_data_length[1]; + if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[1], + &tmp, ~0)) + return FALSE; + + /* don't need to copy tmp out, since key_data_length will be set + by the above encoding. */ + + return(TRUE); +} + +bool_t +xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp) +{ + if (!xdr_array(xdrs, (caddr_t *) &objp->key_data, + (u_int *) &objp->n_key_data, ~0, + sizeof(krb5_key_data), + xdr_krb5_key_data)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp) +{ + switch (xdrs->x_op) { + case XDR_ENCODE: + objp->version = OSA_ADB_PRINC_VERSION_1; + /* fall through */ + case XDR_FREE: + if (!xdr_int(xdrs, &objp->version)) + return FALSE; + break; + case XDR_DECODE: + if (!xdr_int(xdrs, &objp->version)) + return FALSE; + if (objp->version != OSA_ADB_PRINC_VERSION_1) + return FALSE; + break; + } + + if (!xdr_nullstring(xdrs, &objp->policy)) + return (FALSE); + if (!xdr_long(xdrs, &objp->aux_attributes)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->old_key_next)) + return (FALSE); + if (!xdr_krb5_kvno(xdrs, &objp->admin_history_kvno)) + return (FALSE); + if (!xdr_array(xdrs, (caddr_t *) &objp->old_keys, + (unsigned int *) &objp->old_key_len, ~0, + sizeof(osa_pw_hist_ent), + xdr_osa_pw_hist_ent)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp) +{ + switch (xdrs->x_op) { + case XDR_ENCODE: + objp->version = OSA_ADB_POLICY_VERSION_1; + /* fall through */ + case XDR_FREE: + if (!xdr_int(xdrs, &objp->version)) + return FALSE; + break; + case XDR_DECODE: + if (!xdr_int(xdrs, &objp->version)) + return FALSE; + if (objp->version != OSA_ADB_POLICY_VERSION_1) + return FALSE; + break; + } + + if(!xdr_nullstring(xdrs, &objp->name)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->pw_min_life)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->pw_max_life)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->pw_min_length)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->pw_min_classes)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->pw_history_num)) + return (FALSE); + if (!xdr_u_int32(xdrs, &objp->policy_refcnt)) + return (FALSE); + return (TRUE); +} diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h new file mode 100644 index 000000000..1e98430db --- /dev/null +++ b/src/lib/kadm5/admin.h @@ -0,0 +1,649 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#ifndef __KADM5_ADMIN_H__ +#define __KADM5_ADMIN_H__ + +#if !defined(USE_KADM5_API_VERSION) +#define USE_KADM5_API_VERSION 2 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define KADM5_ADMIN_SERVICE "kadmin/admin" +#define KADM5_CHANGEPW_SERVICE "kadmin/changepw" +#define KADM5_HIST_PRINCIPAL "kadmin/history" + +typedef krb5_principal kadm5_princ_t; +typedef char *kadm5_policy_t; +typedef long kadm5_ret_t; + +#define KADM5_PW_FIRST_PROMPT \ + ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) +#define KADM5_PW_SECOND_PROMPT \ + ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) + +/* + * Succsessfull return code + */ +#define KADM5_OK 0 + +/* + * XXX This should be in kdb.h; it is here so that I do not have to + * change that file yet, but this should be *very* temporary. + * --- bjaspan, 5/23/96 + */ +#define KRB5_TL_KADM5_E_DATA 0x0004 + +/* + * Field masks + */ + +/* kadm5_principal_ent_t */ +#define KADM5_PRINCIPAL 0x000001 +#define KADM5_PRINC_EXPIRE_TIME 0x000002 +#define KADM5_PW_EXPIRATION 0x000004 +#define KADM5_LAST_PWD_CHANGE 0x000008 +#define KADM5_ATTRIBUTES 0x000010 +#define KADM5_MAX_LIFE 0x000020 +#define KADM5_MOD_TIME 0x000040 +#define KADM5_MOD_NAME 0x000080 +#define KADM5_KVNO 0x000100 +#define KADM5_MKVNO 0x000200 +#define KADM5_AUX_ATTRIBUTES 0x000400 +#define KADM5_POLICY 0x000800 +#define KADM5_POLICY_CLR 0x001000 +/* version 2 masks */ +#define KADM5_MAX_RLIFE 0x002000 +#define KADM5_LAST_SUCCESS 0x004000 +#define KADM5_LAST_FAILED 0x008000 +#define KADM5_FAIL_AUTH_COUNT 0x010000 +#define KADM5_KEY_DATA 0x020000 +#define KADM5_TL_DATA 0x040000 +/* all but KEY_DATA and TL_DATA */ +#define KADM5_PRINCIPAL_NORMAL_MASK 0x01ffff + +/* kadm5_policy_ent_t */ +#define KADM5_PW_MAX_LIFE 0x004000 +#define KADM5_PW_MIN_LIFE 0x008000 +#define KADM5_PW_MIN_LENGTH 0x010000 +#define KADM5_PW_MIN_CLASSES 0x020000 +#define KADM5_PW_HISTORY_NUM 0x040000 +#define KADM5_REF_COUNT 0x080000 + +/* kadm5_config_params */ +#define KADM5_CONFIG_REALM 0x000001 +#define KADM5_CONFIG_DBNAME 0x000002 +#define KADM5_CONFIG_MKEY_NAME 0x000004 +#define KADM5_CONFIG_MAX_LIFE 0x000008 +#define KADM5_CONFIG_MAX_RLIFE 0x000010 +#define KADM5_CONFIG_EXPIRATION 0x000020 +#define KADM5_CONFIG_FLAGS 0x000040 +#define KADM5_CONFIG_ADMIN_KEYTAB 0x000080 +#define KADM5_CONFIG_STASH_FILE 0x000100 +#define KADM5_CONFIG_ENCTYPE 0x000200 +#define KADM5_CONFIG_ADBNAME 0x000400 +#define KADM5_CONFIG_ADB_LOCKFILE 0x000800 +#define KADM5_CONFIG_PROFILE 0x001000 +#define KADM5_CONFIG_ACL_FILE 0x002000 +#define KADM5_CONFIG_KADMIND_PORT 0x004000 +#define KADM5_CONFIG_ENCTYPES 0x008000 +#define KADM5_CONFIG_ADMIN_SERVER 0x010000 +#define KADM5_CONFIG_DICT_FILE 0x020000 +#define KADM5_CONFIG_MKEY_FROM_KBD 0x040000 + +/* + * permission bits + */ +#define KADM5_PRIV_GET 0x01 +#define KADM5_PRIV_ADD 0x02 +#define KADM5_PRIV_MODIFY 0x04 +#define KADM5_PRIV_DELETE 0x08 + +/* + * API versioning constants + */ +#define KADM5_MASK_BITS 0xffffff00 + +#define KADM5_STRUCT_VERSION_MASK 0x12345600 +#define KADM5_STRUCT_VERSION_1 (KADM5_STRUCT_VERSION_MASK|0x01) +#define KADM5_STRUCT_VERSION KADM5_STRUCT_VERSION_1 + +#define KADM5_API_VERSION_MASK 0x12345700 +#define KADM5_API_VERSION_1 (KADM5_API_VERSION_MASK|0x01) +#define KADM5_API_VERSION_2 (KADM5_API_VERSION_MASK|0x02) + +typedef struct _kadm5_principal_ent_t_v2 { + krb5_principal principal; + krb5_timestamp princ_expire_time; + krb5_timestamp last_pwd_change; + krb5_timestamp pw_expiration; + krb5_deltat max_life; + krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_flags attributes; + krb5_kvno kvno; + krb5_kvno mkvno; + char *policy; + long aux_attributes; + + /* version 2 fields */ + krb5_deltat max_renewable_life; + krb5_timestamp last_success; + krb5_timestamp last_failed; + krb5_kvno fail_auth_count; + krb5_int16 n_key_data; + krb5_int16 n_tl_data; + krb5_tl_data *tl_data; + krb5_key_data *key_data; +} kadm5_principal_ent_rec_v2, *kadm5_principal_ent_t_v2; + +typedef struct _kadm5_principal_ent_t_v1 { + krb5_principal principal; + krb5_timestamp princ_expire_time; + krb5_timestamp last_pwd_change; + krb5_timestamp pw_expiration; + krb5_deltat max_life; + krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_flags attributes; + krb5_kvno kvno; + krb5_kvno mkvno; + char *policy; + long aux_attributes; +} kadm5_principal_ent_rec_v1, *kadm5_principal_ent_t_v1; + +#if USE_KADM5_API_VERSION == 1 +typedef struct _kadm5_principal_ent_t_v1 + kadm5_principal_ent_rec, *kadm5_principal_ent_t; +#else +typedef struct _kadm5_principal_ent_t_v2 + kadm5_principal_ent_rec, *kadm5_principal_ent_t; +#endif + +typedef struct _kadm5_policy_ent_t { + char *policy; + long pw_min_life; + long pw_max_life; + long pw_min_length; + long pw_min_classes; + long pw_history_num; + long policy_refcnt; +} kadm5_policy_ent_rec, *kadm5_policy_ent_t; + +typedef struct __krb5_key_salt_tuple { + krb5_enctype ks_enctype; + krb5_int32 ks_salttype; +} krb5_key_salt_tuple; + +/* + * Data structure returned by kadm5_get_config_params() + */ +typedef struct _kadm5_config_params { + long mask; + char * realm; + char * profile; + int kadmind_port; + + char * admin_server; + + char * dbname; + char * admin_dbname; + char * admin_lockfile; + char * admin_keytab; + char * acl_file; + char * dict_file; + + int mkey_from_kbd; + char * stash_file; + char * mkey_name; + krb5_enctype enctype; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_key_salt_tuple *keysalts; + krb5_int32 num_keysalts; +} kadm5_config_params; + +/*********************************************************************** + * This is the old krb5_realm_read_params, which I mutated into + * kadm5_get_config_params but which old code (kdb5_* and krb5kdc) + * still uses. + ***********************************************************************/ + +/* + * Data structure returned by krb5_read_realm_params() + */ +typedef struct __krb5_realm_params { + char * realm_profile; + char * realm_dbname; + char * realm_mkey_name; + char * realm_stash_file; + char * realm_kdc_ports; + char * realm_acl_file; + krb5_int32 realm_kadmind_port; + krb5_enctype realm_enctype; + krb5_deltat realm_max_life; + krb5_deltat realm_max_rlife; + krb5_timestamp realm_expiration; + krb5_flags realm_flags; + krb5_key_salt_tuple *realm_keysalts; + unsigned int realm_kadmind_port_valid:1; + unsigned int realm_enctype_valid:1; + unsigned int realm_max_life_valid:1; + unsigned int realm_max_rlife_valid:1; + unsigned int realm_expiration_valid:1; + unsigned int realm_flags_valid:1; + unsigned int realm_filler:7; + krb5_int32 realm_num_keysalts; +} krb5_realm_params; + +/* + * functions + */ + +#if USE_KADM5_API_VERSION > 1 +krb5_error_code kadm5_get_config_params(krb5_context context, + char *kdcprofile, char *kdcenv, + kadm5_config_params *params_in, + kadm5_config_params *params_out); +krb5_error_code kadm5_free_realm_params(krb5_context kcontext, + kadm5_config_params *params); +#endif + +kadm5_ret_t kadm5_init(char *client_name, char *pass, + char *service_name, +#if USE_KADM5_API_VERSION == 1 + char *realm, +#else + kadm5_config_params *params, +#endif + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +kadm5_ret_t kadm5_init_with_password(char *client_name, + char *pass, + char *service_name, +#if USE_KADM5_API_VERSION == 1 + char *realm, +#else + kadm5_config_params *params, +#endif + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +kadm5_ret_t kadm5_init_with_skey(char *client_name, + char *keytab, + char *service_name, +#if USE_KADM5_API_VERSION == 1 + char *realm, +#else + kadm5_config_params *params, +#endif + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +#if USE_KADM5_API_VERSION > 1 +kadm5_ret_t kadm5_init_with_creds(char *client_name, + krb5_ccache cc, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +#endif +kadm5_ret_t kadm5_flush(void *server_handle); +kadm5_ret_t kadm5_destroy(void *server_handle); +kadm5_ret_t kadm5_create_principal(void *server_handle, + kadm5_principal_ent_t ent, + long mask, char *pass); +kadm5_ret_t kadm5_delete_principal(void *server_handle, + krb5_principal principal); +kadm5_ret_t kadm5_modify_principal(void *server_handle, + kadm5_principal_ent_t ent, + long mask); +kadm5_ret_t kadm5_rename_principal(void *server_handle, + krb5_principal,krb5_principal); +#if USE_KADM5_API_VERSION == 1 +kadm5_ret_t kadm5_get_principal(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t *ent); +#else +kadm5_ret_t kadm5_get_principal(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t ent, + long mask); +#endif +kadm5_ret_t kadm5_chpass_principal(void *server_handle, + krb5_principal principal, + char *pass); +#if USE_KADM5_API_VERSION == 1 +kadm5_ret_t kadm5_randkey_principal(void *server_handle, + krb5_principal principal, + krb5_keyblock **keyblock); +#else +kadm5_ret_t kadm5_randkey_principal(void *server_handle, + krb5_principal principal, + krb5_keyblock **keyblocks, + int *n_keys); +#endif +kadm5_ret_t kadm5_create_policy(void *server_handle, + kadm5_policy_ent_t ent, + long mask); +/* + * kadm5_create_policy_internal is not part of the supported, + * exposed API. It is available only in the server library, and you + * shouldn't use it unless you know why it's there and how it's + * different from kadm5_create_policy. + */ +kadm5_ret_t kadm5_create_policy_internal(void *server_handle, + kadm5_policy_ent_t + entry, long mask); +kadm5_ret_t kadm5_delete_policy(void *server_handle, + kadm5_policy_t policy); +kadm5_ret_t kadm5_modify_policy(void *server_handle, + kadm5_policy_ent_t ent, + long mask); +/* + * kadm5_modify_policy_internal is not part of the supported, + * exposed API. It is available only in the server library, and you + * shouldn't use it unless you know why it's there and how it's + * different from kadm5_modify_policy. + */ +kadm5_ret_t kadm5_modify_policy_internal(void *server_handle, + kadm5_policy_ent_t + entry, long mask); +#if USE_KADM5_API_VERSION == 1 +kadm5_ret_t kadm5_get_policy(void *server_handle, + kadm5_policy_t policy, + kadm5_policy_ent_t *ent); +#else +kadm5_ret_t kadm5_get_policy(void *server_handle, + kadm5_policy_t policy, + kadm5_policy_ent_t ent); +#endif +kadm5_ret_t kadm5_get_privs(void *server_handle, + long *privs); + +kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret); + +kadm5_ret_t kadm5_free_principal_ent(void *server_handle, + kadm5_principal_ent_t + ent); +kadm5_ret_t kadm5_free_policy_ent(void *server_handle, + kadm5_policy_ent_t ent); + +kadm5_ret_t kadm5_get_principals(void *server_handle, + char *exp, char ***princs, + int *count); + +kadm5_ret_t kadm5_get_policies(void *server_handle, + char *exp, char ***pols, + int *count); + +#if USE_KADM5_API_VERSION > 1 +kadm5_ret_t kadm5_free_key_data(void *server_handle, + krb5_int16 *n_key_data, + krb5_key_data *key_data); +#endif + +#if USE_KADM5_API_VERSION == 1 +/* + * OVSEC_KADM_API_VERSION_1 should be, if possible, compile-time + * compatible with KADM5_API_VERSION_2. Basically, this means we have + * to continue to provide all the old ovsec_kadm function and symbol + * names. + */ + +#define OVSEC_KADM_ACLFILE "/krb5/ovsec_adm.acl" +#define OVSEC_KADM_WORDFILE "/krb5/ovsec_adm.dict" + +#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin" +#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" +#define OVSEC_KADM_HIST_PRINCIPAL "ovsec_adm/history" + +typedef krb5_principal ovsec_kadm_princ_t; +typedef krb5_keyblock ovsec_kadm_keyblock; +typedef char *ovsec_kadm_policy_t; +typedef long ovsec_kadm_ret_t; + +enum ovsec_kadm_salttype { OVSEC_KADM_SALT_V4, OVSEC_KADM_SALT_NORMAL }; +enum ovsec_kadm_saltmod { OVSEC_KADM_MOD_KEEP, OVSEC_KADM_MOD_V4, OVSEC_KADM_MOD_NORMAL }; + +#define OVSEC_KADM_PW_FIRST_PROMPT \ + ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_PROMPT)) +#define OVSEC_KADM_PW_SECOND_PROMPT \ + ((char *) error_message(CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT)) + +/* + * Succsessfull return code + */ +#define OVSEC_KADM_OK 0 + +/* + * Create/Modify masks + */ +/* principal */ +#define OVSEC_KADM_PRINCIPAL 0x000001 +#define OVSEC_KADM_PRINC_EXPIRE_TIME 0x000002 +#define OVSEC_KADM_PW_EXPIRATION 0x000004 +#define OVSEC_KADM_LAST_PWD_CHANGE 0x000008 +#define OVSEC_KADM_ATTRIBUTES 0x000010 +#define OVSEC_KADM_MAX_LIFE 0x000020 +#define OVSEC_KADM_MOD_TIME 0x000040 +#define OVSEC_KADM_MOD_NAME 0x000080 +#define OVSEC_KADM_KVNO 0x000100 +#define OVSEC_KADM_MKVNO 0x000200 +#define OVSEC_KADM_AUX_ATTRIBUTES 0x000400 +#define OVSEC_KADM_POLICY 0x000800 +#define OVSEC_KADM_POLICY_CLR 0x001000 +/* policy */ +#define OVSEC_KADM_PW_MAX_LIFE 0x004000 +#define OVSEC_KADM_PW_MIN_LIFE 0x008000 +#define OVSEC_KADM_PW_MIN_LENGTH 0x010000 +#define OVSEC_KADM_PW_MIN_CLASSES 0x020000 +#define OVSEC_KADM_PW_HISTORY_NUM 0x040000 +#define OVSEC_KADM_REF_COUNT 0x080000 + +/* + * permission bits + */ +#define OVSEC_KADM_PRIV_GET 0x01 +#define OVSEC_KADM_PRIV_ADD 0x02 +#define OVSEC_KADM_PRIV_MODIFY 0x04 +#define OVSEC_KADM_PRIV_DELETE 0x08 + +/* + * API versioning constants + */ +#define OVSEC_KADM_MASK_BITS 0xffffff00 + +#define OVSEC_KADM_STRUCT_VERSION_MASK 0x12345600 +#define OVSEC_KADM_STRUCT_VERSION_1 (OVSEC_KADM_STRUCT_VERSION_MASK|0x01) +#define OVSEC_KADM_STRUCT_VERSION OVSEC_KADM_STRUCT_VERSION_1 + +#define OVSEC_KADM_API_VERSION_MASK 0x12345700 +#define OVSEC_KADM_API_VERSION_1 (OVSEC_KADM_API_VERSION_MASK|0x01) + + +typedef struct _ovsec_kadm_principal_ent_t { + krb5_principal principal; + krb5_timestamp princ_expire_time; + krb5_timestamp last_pwd_change; + krb5_timestamp pw_expiration; + krb5_deltat max_life; + krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_flags attributes; + krb5_kvno kvno; + krb5_kvno mkvno; + char *policy; + long aux_attributes; +} ovsec_kadm_principal_ent_rec, *ovsec_kadm_principal_ent_t; + +typedef struct _ovsec_kadm_policy_ent_t { + char *policy; + long pw_min_life; + long pw_max_life; + long pw_min_length; + long pw_min_classes; + long pw_history_num; + long policy_refcnt; +} ovsec_kadm_policy_ent_rec, *ovsec_kadm_policy_ent_t; + +/* + * functions + */ +ovsec_kadm_ret_t ovsec_kadm_init(char *client_name, char *pass, + char *service_name, char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +ovsec_kadm_ret_t ovsec_kadm_init_with_password(char *client_name, + char *pass, + char *service_name, + char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +ovsec_kadm_ret_t ovsec_kadm_init_with_skey(char *client_name, + char *keytab, + char *service_name, + char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); +ovsec_kadm_ret_t ovsec_kadm_flush(void *server_handle); +ovsec_kadm_ret_t ovsec_kadm_destroy(void *server_handle); +ovsec_kadm_ret_t ovsec_kadm_create_principal(void *server_handle, + ovsec_kadm_principal_ent_t ent, + long mask, char *pass); +ovsec_kadm_ret_t ovsec_kadm_delete_principal(void *server_handle, + krb5_principal principal); +ovsec_kadm_ret_t ovsec_kadm_modify_principal(void *server_handle, + ovsec_kadm_principal_ent_t ent, + long mask); +ovsec_kadm_ret_t ovsec_kadm_rename_principal(void *server_handle, + krb5_principal,krb5_principal); +ovsec_kadm_ret_t ovsec_kadm_get_principal(void *server_handle, + krb5_principal principal, + ovsec_kadm_principal_ent_t *ent); +ovsec_kadm_ret_t ovsec_kadm_chpass_principal(void *server_handle, + krb5_principal principal, + char *pass); +ovsec_kadm_ret_t ovsec_kadm_randkey_principal(void *server_handle, + krb5_principal principal, + krb5_keyblock **keyblock); +ovsec_kadm_ret_t ovsec_kadm_create_policy(void *server_handle, + ovsec_kadm_policy_ent_t ent, + long mask); +/* + * ovsec_kadm_create_policy_internal is not part of the supported, + * exposed API. It is available only in the server library, and you + * shouldn't use it unless you know why it's there and how it's + * different from ovsec_kadm_create_policy. + */ +ovsec_kadm_ret_t ovsec_kadm_create_policy_internal(void *server_handle, + ovsec_kadm_policy_ent_t + entry, long mask); +ovsec_kadm_ret_t ovsec_kadm_delete_policy(void *server_handle, + ovsec_kadm_policy_t policy); +ovsec_kadm_ret_t ovsec_kadm_modify_policy(void *server_handle, + ovsec_kadm_policy_ent_t ent, + long mask); +/* + * ovsec_kadm_modify_policy_internal is not part of the supported, + * exposed API. It is available only in the server library, and you + * shouldn't use it unless you know why it's there and how it's + * different from ovsec_kadm_modify_policy. + */ +ovsec_kadm_ret_t ovsec_kadm_modify_policy_internal(void *server_handle, + ovsec_kadm_policy_ent_t + entry, long mask); +ovsec_kadm_ret_t ovsec_kadm_get_policy(void *server_handle, + ovsec_kadm_policy_t policy, + ovsec_kadm_policy_ent_t *ent); +ovsec_kadm_ret_t ovsec_kadm_get_privs(void *server_handle, + long *privs); + +ovsec_kadm_ret_t ovsec_kadm_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret); + +ovsec_kadm_ret_t ovsec_kadm_free_principal_ent(void *server_handle, + ovsec_kadm_principal_ent_t + ent); +ovsec_kadm_ret_t ovsec_kadm_free_policy_ent(void *server_handle, + ovsec_kadm_policy_ent_t ent); + +ovsec_kadm_ret_t ovsec_kadm_get_principals(void *server_handle, + char *exp, char ***princs, + int *count); + +ovsec_kadm_ret_t ovsec_kadm_get_policies(void *server_handle, + char *exp, char ***pols, + int *count); + +#define OVSEC_KADM_FAILURE KADM5_FAILURE +#define OVSEC_KADM_AUTH_GET KADM5_AUTH_GET +#define OVSEC_KADM_AUTH_ADD KADM5_AUTH_ADD +#define OVSEC_KADM_AUTH_MODIFY KADM5_AUTH_MODIFY +#define OVSEC_KADM_AUTH_DELETE KADM5_AUTH_DELETE +#define OVSEC_KADM_AUTH_INSUFFICIENT KADM5_AUTH_INSUFFICIENT +#define OVSEC_KADM_BAD_DB KADM5_BAD_DB +#define OVSEC_KADM_DUP KADM5_DUP +#define OVSEC_KADM_RPC_ERROR KADM5_RPC_ERROR +#define OVSEC_KADM_NO_SRV KADM5_NO_SRV +#define OVSEC_KADM_BAD_HIST_KEY KADM5_BAD_HIST_KEY +#define OVSEC_KADM_NOT_INIT KADM5_NOT_INIT +#define OVSEC_KADM_UNK_PRINC KADM5_UNK_PRINC +#define OVSEC_KADM_UNK_POLICY KADM5_UNK_POLICY +#define OVSEC_KADM_BAD_MASK KADM5_BAD_MASK +#define OVSEC_KADM_BAD_CLASS KADM5_BAD_CLASS +#define OVSEC_KADM_BAD_LENGTH KADM5_BAD_LENGTH +#define OVSEC_KADM_BAD_POLICY KADM5_BAD_POLICY +#define OVSEC_KADM_BAD_PRINCIPAL KADM5_BAD_PRINCIPAL +#define OVSEC_KADM_BAD_AUX_ATTR KADM5_BAD_AUX_ATTR +#define OVSEC_KADM_BAD_HISTORY KADM5_BAD_HISTORY +#define OVSEC_KADM_BAD_MIN_PASS_LIFE KADM5_BAD_MIN_PASS_LIFE +#define OVSEC_KADM_PASS_Q_TOOSHORT KADM5_PASS_Q_TOOSHORT +#define OVSEC_KADM_PASS_Q_CLASS KADM5_PASS_Q_CLASS +#define OVSEC_KADM_PASS_Q_DICT KADM5_PASS_Q_DICT +#define OVSEC_KADM_PASS_REUSE KADM5_PASS_REUSE +#define OVSEC_KADM_PASS_TOOSOON KADM5_PASS_TOOSOON +#define OVSEC_KADM_POLICY_REF KADM5_POLICY_REF +#define OVSEC_KADM_INIT KADM5_INIT +#define OVSEC_KADM_BAD_PASSWORD KADM5_BAD_PASSWORD +#define OVSEC_KADM_PROTECT_PRINCIPAL KADM5_PROTECT_PRINCIPAL +#define OVSEC_KADM_BAD_SERVER_HANDLE KADM5_BAD_SERVER_HANDLE +#define OVSEC_KADM_BAD_STRUCT_VERSION KADM5_BAD_STRUCT_VERSION +#define OVSEC_KADM_OLD_STRUCT_VERSION KADM5_OLD_STRUCT_VERSION +#define OVSEC_KADM_NEW_STRUCT_VERSION KADM5_NEW_STRUCT_VERSION +#define OVSEC_KADM_BAD_API_VERSION KADM5_BAD_API_VERSION +#define OVSEC_KADM_OLD_LIB_API_VERSION KADM5_OLD_LIB_API_VERSION +#define OVSEC_KADM_OLD_SERVER_API_VERSION KADM5_OLD_SERVER_API_VERSION +#define OVSEC_KADM_NEW_LIB_API_VERSION KADM5_NEW_LIB_API_VERSION +#define OVSEC_KADM_NEW_SERVER_API_VERSION KADM5_NEW_SERVER_API_VERSION +#define OVSEC_KADM_SECURE_PRINC_MISSING KADM5_SECURE_PRINC_MISSING +#define OVSEC_KADM_NO_RENAME_SALT KADM5_NO_RENAME_SALT + +#endif /* USE_KADM5_API_VERSION == 1 */ + +#endif /* __KADM5_ADMIN_H__ */ diff --git a/src/lib/kadm5/admin_internal.h b/src/lib/kadm5/admin_internal.h new file mode 100644 index 000000000..d73837e67 --- /dev/null +++ b/src/lib/kadm5/admin_internal.h @@ -0,0 +1,79 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#ifndef __KADM5_ADMIN_INTERNAL_H__ +#define __KADM5_ADMIN_INTERNAL_H__ + +#include + +#define KADM5_SERVER_HANDLE_MAGIC 0x12345800 + +#define GENERIC_CHECK_HANDLE(handle, old_api_version, new_api_version) \ +{ \ + kadm5_server_handle_t srvr = \ + (kadm5_server_handle_t) handle; \ + \ + if (! srvr) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (srvr->magic_number != KADM5_SERVER_HANDLE_MAGIC) \ + return KADM5_BAD_SERVER_HANDLE; \ + if ((srvr->struct_version & KADM5_MASK_BITS) != \ + KADM5_STRUCT_VERSION_MASK) \ + return KADM5_BAD_STRUCT_VERSION; \ + if (srvr->struct_version < KADM5_STRUCT_VERSION_1) \ + return KADM5_OLD_STRUCT_VERSION; \ + if (srvr->struct_version > KADM5_STRUCT_VERSION_1) \ + return KADM5_NEW_STRUCT_VERSION; \ + if ((srvr->api_version & KADM5_MASK_BITS) != \ + KADM5_API_VERSION_MASK) \ + return KADM5_BAD_API_VERSION; \ + if (srvr->api_version < KADM5_API_VERSION_1) \ + return old_api_version; \ + if (srvr->api_version > KADM5_API_VERSION_2) \ + return new_api_version; \ +} + +/* + * _KADM5_CHECK_HANDLE calls the function _kadm5_check_handle and + * returns any non-zero error code that function returns. + * _kadm5_check_handle, in client_handle.c and server_handle.c, exists + * in both the server- and client- side libraries. In each library, + * it calls CHECK_HANDLE, which is defined by the appropriate + * _internal.h header file to call GENERIC_CHECK_HANDLE as well as + * CLIENT_CHECK_HANDLE and SERVER_CHECK_HANDLE. + * + * _KADM5_CHECK_HANDLE should be used by a function that needs to + * check the handle but wants to be the same code in both the client + * and server library; it makes a function call to the right handle + * checker. Code that only exists in one library can call the + * CHECK_HANDLE macro, which inlines the test instead of making + * another function call. + * + * Got that? + */ +#define _KADM5_CHECK_HANDLE(handle) \ +{ int code; if (code = _kadm5_check_handle((void *)handle)) return code; } + +kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, + void *lhandle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret); + +/* this is needed by the alt_prof code I stole. The functions + maybe shouldn't be named krb5_*, but they are. */ + +krb5_error_code +krb5_string_to_keysalts(char *string, const char *tupleseps, + const char *ksaltseps, krb5_boolean dups, + krb5_key_salt_tuple **ksaltp, krb5_int32 *nksaltp); + +krb5_error_code +krb5_string_to_flags(char* string, const char* positive, const char* negative, + krb5_flags *flagsp); + +#endif /* __KADM5_ADMIN_INTERNAL_H__ */ diff --git a/src/lib/kadm5/admin_xdr.h b/src/lib/kadm5/admin_xdr.h new file mode 100644 index 000000000..3e4f48f7a --- /dev/null +++ b/src/lib/kadm5/admin_xdr.h @@ -0,0 +1,65 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.5 1996/07/22 20:35:33 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.4.4.1 1996/07/18 03:08:25 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.4.2.1 1996/06/20 02:16:37 marc + * File added to the repository on a branch + * + * Revision 1.4 1996/05/30 16:36:34 bjaspan + * finish updating to kadm5 naming (oops) + * + * Revision 1.3 1996/05/22 00:28:19 bjaspan + * rename to kadm5 + * + * Revision 1.2 1996/05/12 06:30:10 marc + * - fixup includes and data types to match beta6 + * + * Revision 1.1 1993/11/09 04:06:01 shanzer + * Initial revision + * + */ + +#include +#include "kadm_rpc.h" + +bool_t xdr_nullstring(XDR *xdrs, char **objp); +bool_t xdr_krb5_timestamp(XDR *xdrs, krb5_timestamp *objp); +bool_t xdr_krb5_kvno(XDR *xdrs, krb5_kvno *objp); +bool_t xdr_krb5_deltat(XDR *xdrs, krb5_deltat *objp); +bool_t xdr_krb5_flags(XDR *xdrs, krb5_flags *objp); +bool_t xdr_kadm5_ret_t(XDR *xdrs, kadm5_ret_t *objp); +bool_t xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp); +bool_t xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp); +bool_t xdr_kadm5_policy_ent_t(XDR *xdrs, kadm5_policy_ent_t *objp); +bool_t xdr_kadm5_principal_ent_t(XDR *xdrs, kadm5_principal_ent_t *objp); +bool_t xdr_cprinc_arg(XDR *xdrs, cprinc_arg *objp); +bool_t xdr_dprinc_arg(XDR *xdrs, dprinc_arg *objp); +bool_t xdr_mprinc_arg(XDR *xdrs, mprinc_arg *objp); +bool_t xdr_rprinc_arg(XDR *xdrs, rprinc_arg *objp); +bool_t xdr_chpass_arg(XDR *xdrs, chpass_arg *objp); +bool_t xdr_chrand_arg(XDR *xdrs, chrand_arg *objp); +bool_t xdr_chrand_ret(XDR *xdrs, chrand_ret *objp); +bool_t xdr_gprinc_arg(XDR *xdrs, gprinc_arg *objp); +bool_t xdr_gprinc_arg(XDR *xdrs, gprinc_arg *objp); +bool_t xdr_cpol_arg(XDR *xdrs, cpol_arg *objp); +bool_t xdr_dpol_arg(XDR *xdrs, dpol_arg *objp); +bool_t xdr_mpol_arg(XDR *xdrs, mpol_arg *objp); +bool_t xdr_gpol_arg(XDR *xdrs, gpol_arg *objp); +bool_t xdr_gpol_ret(XDR *xdrs, gpol_ret *objp); +bool_t xdr_krb5_principal(XDR *xdrs, krb5_principal *objp); +bool_t xdr_krb5_octet(XDR *xdrs, krb5_octet *objp); +bool_t xdr_krb5_int32(XDR *xdrs, krb5_int32 *objp); +bool_t xdr_krb5_enctype(XDR *xdrs, krb5_enctype *objp); +bool_t xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp); diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c new file mode 100644 index 000000000..2f36f76fa --- /dev/null +++ b/src/lib/kadm5/alt_prof.c @@ -0,0 +1,861 @@ +/* + * lib/kadm/alt_prof.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. + * + */ + +/* + * alt_prof.c - Implement alternate profile file handling. + */ +#include "k5-int.h" +#include +#include +#include + +/* + * krb5_aprof_init() - Initialize alternate profile context. + * + * Parameters: + * fname - default file name of the profile. + * envname - environment variable name which can override fname. + * acontextp - Pointer to opaque context for alternate profile. + * + * Returns: + * error codes from profile_init() + */ +krb5_error_code +krb5_aprof_init(fname, envname, acontextp) + char *fname; + char *envname; + krb5_pointer *acontextp; +{ + krb5_error_code kret; + const char *namelist[2]; + profile_t profile; + + namelist[1] = (char *) NULL; + profile = (profile_t) NULL; + if (envname) { + if ((namelist[0] = getenv(envname))) { + if (!(kret = profile_init(namelist, &profile))) { + *acontextp = (krb5_pointer) profile; + return(0); + } + } + } + namelist[0] = fname; + profile = (profile_t) NULL; + if (!(kret = profile_init(namelist, &profile))) { + *acontextp = (krb5_pointer) profile; + return(0); + } + return(kret); +} + +/* + * krb5_aprof_getvals() - Get values from alternate profile. + * + * Parameters: + * acontext - opaque context for alternate profile. + * hierarchy - hierarchy of value to retrieve. + * retdata - Returned data values. + * + * Returns: + * error codes from profile_get_values() + */ +krb5_error_code +krb5_aprof_getvals(acontext, hierarchy, retdata) + krb5_pointer acontext; + const char **hierarchy; + char ***retdata; +{ + return(profile_get_values((profile_t) acontext, + hierarchy, + retdata)); +} + +/* + * krb5_aprof_get_deltat() - Get a delta time value from the alternate + * profile. + * + * Parameters: + * acontext - opaque context for alternate profile. + * hierarchy - hierarchy of value to retrieve. + * uselast - if true, use last value, otherwise use + * first value found. + * deltatp - returned delta time value. + * + * Returns: + * error codes from profile_get_values() + * error codes from krb5_string_to_deltat() + */ +krb5_error_code +krb5_aprof_get_deltat(acontext, hierarchy, uselast, deltatp) + krb5_pointer acontext; + const char **hierarchy; + krb5_boolean uselast; + krb5_deltat *deltatp; +{ + krb5_error_code kret; + char **values; + char *valp; + int index; + + if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { + index = 0; + if (uselast) { + for (index=0; values[index]; index++); + index--; + } + valp = values[index]; + kret = krb5_string_to_deltat(valp, deltatp); + + /* Free the string storage */ + for (index=0; values[index]; index++) + krb5_xfree(values[index]); + krb5_xfree(values); + } + return(kret); +} + +/* + * krb5_aprof_get_string() - Get a string value from the alternate + * profile. + * + * Parameters: + * acontext - opaque context for alternate profile. + * hierarchy - hierarchy of value to retrieve. + * uselast - if true, use last value, otherwise use + * first value found. + * stringp - returned string value. + * + * Returns: + * error codes from profile_get_values() + */ +krb5_error_code +krb5_aprof_get_string(acontext, hierarchy, uselast, stringp) + krb5_pointer acontext; + const char **hierarchy; + krb5_boolean uselast; + char **stringp; +{ + krb5_error_code kret; + char **values; + int index, i; + + if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { + index = 0; + if (uselast) { + for (index=0; values[index]; index++); + index--; + } + + *stringp = values[index]; + + /* Free the string storage */ + for (i=0; values[i]; i++) + if (i != index) + krb5_xfree(values[i]); + krb5_xfree(values); + } + return(kret); +} + +/* + * krb5_aprof_get_int32() - Get a 32-bit integer value from the alternate + * profile. + * + * Parameters: + * acontext - opaque context for alternate profile. + * hierarchy - hierarchy of value to retrieve. + * uselast - if true, use last value, otherwise use + * first value found. + * intp - returned 32-bit integer value. + * + * Returns: + * error codes from profile_get_values() + * EINVAL - value is not an integer + */ +krb5_error_code +krb5_aprof_get_int32(acontext, hierarchy, uselast, intp) + krb5_pointer acontext; + const char **hierarchy; + krb5_boolean uselast; + krb5_int32 *intp; +{ + krb5_error_code kret; + char **values; + int index; + + if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) { + index = 0; + if (uselast) { + for (index=0; values[index]; index++); + index--; + } + + if (sscanf(values[index], "%d", intp) != 1) + kret = EINVAL; + + /* Free the string storage */ + for (index=0; values[index]; index++) + krb5_xfree(values[index]); + krb5_xfree(values); + } + return(kret); +} + +/* + * krb5_aprof_finish() - Finish alternate profile context. + * + * Parameter: + * acontext - opaque context for alternate profile. + * + * Returns: + * 0 on success, something else on failure. + */ +krb5_error_code +krb5_aprof_finish(acontext) + krb5_pointer acontext; +{ + profile_release(acontext); + return(0); +} + +/* + * Function: kadm5_get_config_params + * + * Purpose: Merge configuration parameters provided by the caller with + * values specified in configuration files and with default values. + * + * Arguments: + * + * context (r) krb5_context to use + * profile (r) profile file to use + * envname (r) envname that contains a profile name to + * override profile + * params_in (r) params structure containing user-supplied + * values, or NULL + * params_out (w) params structure to be filled in + * + * Effects: + * + * The fields and mask of params_out are filled in with values + * obtained from params_in, the specified profile, and default + * values. Only and all fields specified in params_out->mask are + * set. The context of params_out must be freed with + * kadm5_free_config_params. + * + * params_in and params_out may be the same pointer. However, all pointers + * in params_in for which the mask is set will be re-assigned to newly copied + * versions, overwriting the old pointer value. + */ +krb5_error_code kadm5_get_config_params(context, kdcprofile, kdcenv, + params_in, params_out) + krb5_context context; + char *kdcprofile; + char *kdcenv; + kadm5_config_params *params_in, *params_out; +{ + char *filename; + char *envname; + char *lrealm; + krb5_pointer aprofile = 0; + const char *hierarchy[4]; + char *svalue; + krb5_int32 ivalue; + krb5_deltat dtvalue; + kadm5_config_params params, empty_params; + + krb5_error_code kret; + + memset((char *) ¶ms, 0, sizeof(params)); + memset((char *) &empty_params, 0, sizeof(empty_params)); + + if (params_in == NULL) params_in = &empty_params; + + if (params_in->mask & KADM5_CONFIG_REALM) { + lrealm = params.realm = strdup(params_in->realm); + params.mask |= KADM5_CONFIG_REALM; + } else { + kret = krb5_get_default_realm(context, &lrealm); + if (kret) + goto cleanup; + params.realm = lrealm; + params.mask |= KADM5_CONFIG_REALM; + } + if (params_in->mask & KADM5_CONFIG_PROFILE) { + filename = params.profile = strdup(params_in->profile); + params.mask |= KADM5_CONFIG_PROFILE; + envname = NULL; + } else { + /* XXX ummm... these defaults should to work on both sides */ + filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE; + envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV; + if (context->profile_secure == TRUE) envname = 0; + } + + kret = krb5_aprof_init(filename, envname, &aprofile); + if (kret) + goto cleanup; + + /* Initialize realm parameters */ + hierarchy[0] = "realms"; + hierarchy[1] = lrealm; + hierarchy[3] = (char *) NULL; + + /* Get the value for the admin server */ + hierarchy[2] = "admin_server"; + if (params_in->mask & KADM5_CONFIG_ADMIN_SERVER) { + params.mask |= KADM5_CONFIG_ADMIN_SERVER; + params.admin_server = strdup(params_in->admin_server); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.admin_server = svalue; + params.mask |= KADM5_CONFIG_ADMIN_SERVER; + } + if (params.mask & KADM5_CONFIG_ADMIN_SERVER) { + char *p; + if (p = strchr(params.admin_server, ':')) { + params.kadmind_port = atoi(p+1); + params.mask |= KADM5_CONFIG_KADMIND_PORT; + *p = '\0'; + } + } + + /* Get the value for the database */ + hierarchy[2] = "database_name"; + if (params_in->mask & KADM5_CONFIG_DBNAME) { + params.mask |= KADM5_CONFIG_DBNAME; + params.dbname = strdup(params_in->dbname); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.dbname = svalue; + params.mask |= KADM5_CONFIG_DBNAME; + } + + /* + * Get the value for the admin (policy) database and lockfile. + * The logic here is slightly tricky. DBNAME, ADBNAME, and + * ADB_LOCKFILE are dependent on the earlier items in the + * sequence. If an earlier item was specified via the input + * parameters, that value overrides the variables in the config + * file and causes the later item to be set to ".kadm5" or + * ".lock", respectively. However, if no earlier item was + * specified, the variables in the config file are used, and the + * ".kadm5" and ".lock" suffixes are only added as a no-variable + * default. + * + * Read the spec. + */ + hierarchy[2] = "admin_database_name"; + if (params_in->mask & KADM5_CONFIG_ADBNAME) { + params.mask |= KADM5_CONFIG_ADBNAME; + params.admin_dbname = strdup(params_in->admin_dbname); + } else if (params_in->mask & KADM5_CONFIG_DBNAME) { + params.admin_dbname = (char *) malloc(strlen(params.dbname) + 6); + if (params.admin_dbname) { + sprintf(params.admin_dbname, "%s.kadm5", params.dbname); + params.mask |= KADM5_CONFIG_ADBNAME; + } + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.admin_dbname = svalue; + params.mask |= KADM5_CONFIG_ADBNAME; + } else if (params.mask & KADM5_CONFIG_DBNAME) { + params.admin_dbname = (char *) malloc(strlen(params.dbname) + 6); + if (params.admin_dbname) { + sprintf(params.admin_dbname, "%s.kadm5", params.dbname); + params.mask |= KADM5_CONFIG_ADBNAME; + } + } + + /* Get the value for the admin (policy) database lock file*/ + hierarchy[2] = "admin_database_lockfile"; + if (params_in->mask & KADM5_CONFIG_ADB_LOCKFILE) { + params.mask |= KADM5_CONFIG_ADB_LOCKFILE; + params.admin_lockfile = strdup(params_in->admin_lockfile); + } else if ((params_in->mask & KADM5_CONFIG_ADBNAME) || + (params_in->mask & KADM5_CONFIG_DBNAME)) { + /* if DBNAME is set but ADBNAME is not, then admin_database + * will already have been set above */ + params.admin_lockfile = (char *) malloc(strlen(params.admin_dbname) + + 6); + if (params.admin_lockfile) { + sprintf(params.admin_lockfile, "%s.lock", params.admin_dbname); + params.mask |= KADM5_CONFIG_ADB_LOCKFILE; + } + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_ADB_LOCKFILE; + params.admin_lockfile = svalue; + } else if (params.mask & KADM5_CONFIG_ADBNAME) { + params.admin_lockfile = (char *) malloc(strlen(params.admin_dbname) + + 6); + if (params.admin_lockfile) { + sprintf(params.admin_lockfile, "%s.lock", params.admin_dbname); + params.mask |= KADM5_CONFIG_ADB_LOCKFILE; + } + } + + /* Get the value for the admin (policy) database lock file*/ + hierarchy[2] = "admin_keytab"; + if (params_in->mask & KADM5_CONFIG_ADMIN_KEYTAB) { + params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; + params.admin_keytab = strdup(params_in->admin_keytab); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; + params.admin_keytab = svalue; + } else { + params.admin_keytab = (char *) getenv("KRB5_KTNAME"); + if (params.admin_keytab) { + params.admin_keytab = strdup(params.admin_keytab); + if (params.admin_keytab) + params.mask |= KADM5_CONFIG_ADMIN_KEYTAB; + } + } + + /* Get the name of the acl file */ + hierarchy[2] = "acl_file"; + if (params_in->mask & KADM5_CONFIG_ACL_FILE) { + params.mask |= KADM5_CONFIG_ACL_FILE; + params.acl_file = strdup(params_in->acl_file); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_ACL_FILE; + params.acl_file = svalue; + } + + /* Get the name of the dict file */ + hierarchy[2] = "dict_file"; + if (params_in->mask & KADM5_CONFIG_DICT_FILE) { + params.mask |= KADM5_CONFIG_DICT_FILE; + params.dict_file = strdup(params_in->dict_file); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_DICT_FILE; + params.dict_file = svalue; + } + + /* Get the value for the kadmind port */ + if (! (params.mask & KADM5_CONFIG_KADMIND_PORT)) { + hierarchy[2] = "kadmind_port"; + if (params_in->mask & KADM5_CONFIG_KADMIND_PORT) { + params.mask |= KADM5_CONFIG_KADMIND_PORT; + params.kadmind_port = params_in->kadmind_port; + } else if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, + &ivalue)) { + params.kadmind_port = ivalue; + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } else { + params.kadmind_port = 749; /* assigned by IANA */ + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } + } + + /* Get the value for the master key name */ + hierarchy[2] = "master_key_name"; + if (params_in->mask & KADM5_CONFIG_MKEY_NAME) { + params.mask |= KADM5_CONFIG_MKEY_NAME; + params.mkey_name = strdup(params_in->mkey_name); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_MKEY_NAME; + params.mkey_name = svalue; + } + + /* Get the value for the master key type */ + hierarchy[2] = "master_key_type"; + if (params_in->mask & KADM5_CONFIG_ENCTYPE) { + params.mask |= KADM5_CONFIG_ENCTYPE; + params.enctype = params_in->enctype; + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + if (!krb5_string_to_enctype(svalue, ¶ms.enctype)) { + params.mask |= KADM5_CONFIG_ENCTYPE; + krb5_xfree(svalue); + } + } + + /* Get the value for mkey_from_kbd */ + if (params_in->mask & KADM5_CONFIG_MKEY_FROM_KBD) { + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + params.mkey_from_kbd = params_in->mkey_from_kbd; + } + + /* Get the value for the stashfile */ + hierarchy[2] = "key_stash_file"; + if (params_in->mask & KADM5_CONFIG_STASH_FILE) { + params.mask |= KADM5_CONFIG_STASH_FILE; + params.stash_file = strdup(params_in->stash_file); + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.mask |= KADM5_CONFIG_STASH_FILE; + params.stash_file = svalue; + } + + /* Get the value for maximum ticket lifetime. */ + hierarchy[2] = "max_life"; + if (params_in->mask & KADM5_CONFIG_MAX_LIFE) { + params.mask |= KADM5_CONFIG_MAX_LIFE; + params.max_life = params_in->max_life; + } else if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { + params.max_life = dtvalue; + params.mask |= KADM5_CONFIG_MAX_LIFE; + } else { + params.max_life = 0; + params.mask |= KADM5_CONFIG_MAX_LIFE; + } + + /* Get the value for maximum renewable ticket lifetime. */ + hierarchy[2] = "max_renewable_life"; + if (params_in->mask & KADM5_CONFIG_MAX_RLIFE) { + params.mask |= KADM5_CONFIG_MAX_RLIFE; + params.max_rlife = params_in->max_rlife; + } else if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { + params.max_rlife = dtvalue; + params.mask |= KADM5_CONFIG_MAX_RLIFE; + } else { + params.max_rlife = 0; + params.mask |= KADM5_CONFIG_MAX_RLIFE; + } + + /* Get the value for the default principal expiration */ + hierarchy[2] = "default_principal_expiration"; + if (params_in->mask & KADM5_CONFIG_EXPIRATION) { + params.mask |= KADM5_CONFIG_EXPIRATION; + params.expiration = params_in->expiration; + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + if (!krb5_string_to_timestamp(svalue, ¶ms.expiration)) { + params.mask |= KADM5_CONFIG_EXPIRATION; + krb5_xfree(svalue); + } + } else { + params.mask |= KADM5_CONFIG_EXPIRATION; + params.expiration = 0; + } + + /* Get the value for the default principal flags */ + hierarchy[2] = "default_principal_flags"; + if (params_in->mask & KADM5_CONFIG_FLAGS) { + params.mask |= KADM5_CONFIG_FLAGS; + params.flags = params_in->flags; + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + char *sp, *ep, *tp; + + sp = svalue; + params.flags = 0; + while (sp) { + if ((ep = strchr(sp, (int) ',')) || + (ep = strchr(sp, (int) ' ')) || + (ep = strchr(sp, (int) '\t'))) { + /* Fill in trailing whitespace of sp */ + tp = ep - 1; + while (isspace(*tp) && (tp < sp)) { + *tp = '\0'; + tp--; + } + *ep = '\0'; + ep++; + /* Skip over trailing whitespace of ep */ + while (isspace(*ep) && (*ep)) ep++; + } + /* Convert this flag */ + if (krb5_string_to_flags(sp, + "+", + "-", + ¶ms.flags)) + break; + sp = ep; + } + if (!sp) + params.mask |= KADM5_CONFIG_FLAGS; + krb5_xfree(svalue); + } else { + params.mask |= KADM5_CONFIG_FLAGS; + params.flags = KRB5_KDB_DEF_FLAGS; + } + + /* Get the value for the supported enctype/salttype matrix */ + hierarchy[2] = "supported_enctypes"; + if (params_in->mask & KADM5_CONFIG_ENCTYPES) { + params.mask |= KADM5_CONFIG_ENCTYPES; + params.keysalts = params_in->keysalts; + params.num_keysalts = params_in->num_keysalts; + } else if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + params.keysalts = NULL; + params.num_keysalts = 0; + krb5_string_to_keysalts(svalue, + ", \t", /* Tuple separators */ + ":.-", /* Key/salt separators */ + 0, /* No duplicates */ + ¶ms.keysalts, + ¶ms.num_keysalts); + if (params.num_keysalts) + params.mask |= KADM5_CONFIG_ENCTYPES; + krb5_xfree(svalue); + } + + *params_out = params; + +cleanup: + if (aprofile) + krb5_aprof_finish(aprofile); + if (kret) { + kadm5_free_config_params(context, ¶ms); + params_out->mask = 0; + } + return(kret); +} +/* + * kadm5_free_config_params() - Free data allocated by above. + */ +krb5_error_code +kadm5_free_config_params(context, params) + krb5_context context; + kadm5_config_params *params; +{ + if (params) { + if (params->profile) + krb5_xfree(params->profile); + if (params->dbname) + krb5_xfree(params->dbname); + if (params->mkey_name) + krb5_xfree(params->mkey_name); + if (params->stash_file) + krb5_xfree(params->stash_file); + if (params->keysalts) + krb5_xfree(params->keysalts); + if (params->admin_keytab) + free(params->admin_keytab); + if (params->dict_file) + free(params->dict_file); + if (params->acl_file) + free(params->acl_file); + if (params->realm) + free(params->realm); + if (params->admin_dbname) + free(params->admin_dbname); + } + return(0); +} + +/*********************************************************************** + * This is the old krb5_realm_read_params, which I mutated into + * kadm5_get_config_params but which old code (kdb5_* and krb5kdc) + * still uses. + ***********************************************************************/ + +/* + * krb5_read_realm_params() - Read per-realm parameters from KDC + * alternate profile. + */ +krb5_error_code +krb5_read_realm_params(kcontext, realm, kdcprofile, kdcenv, rparamp) + krb5_context kcontext; + char *realm; + char *kdcprofile; + char *kdcenv; + krb5_realm_params **rparamp; +{ + char *filename; + char *envname; + char *lrealm; + krb5_pointer aprofile = 0; + krb5_realm_params *rparams; + const char *hierarchy[4]; + char *svalue; + krb5_int32 ivalue; + krb5_deltat dtvalue; + + krb5_error_code kret; + + filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE; + envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV; + + if (kcontext->profile_secure == TRUE) envname = 0; + + rparams = (krb5_realm_params *) NULL; + if (realm) + lrealm = strdup(realm); + else { + kret = krb5_get_default_realm(kcontext, &lrealm); + if (kret) + goto cleanup; + } + + kret = krb5_aprof_init(filename, envname, &aprofile); + if (kret) + goto cleanup; + + rparams = (krb5_realm_params *) malloc(sizeof(krb5_realm_params)); + if (rparams == 0) { + kret = ENOMEM; + goto cleanup; + } + + /* Initialize realm parameters */ + memset((char *) rparams, 0, sizeof(krb5_realm_params)); + + /* Get the value for the database */ + hierarchy[0] = "realms"; + hierarchy[1] = lrealm; + hierarchy[2] = "database_name"; + hierarchy[3] = (char *) NULL; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_dbname = svalue; + + /* Get the value for the KDC port list */ + hierarchy[2] = "kdc_ports"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_kdc_ports = svalue; + + /* Get the name of the acl file */ + hierarchy[2] = "acl_file"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_acl_file = svalue; + + /* Get the value for the kadmind port */ + hierarchy[2] = "kadmind_port"; + if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) { + rparams->realm_kadmind_port = ivalue; + rparams->realm_kadmind_port_valid = 1; + } + + /* Get the value for the master key name */ + hierarchy[2] = "master_key_name"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_mkey_name = svalue; + + /* Get the value for the master key type */ + hierarchy[2] = "master_key_type"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + if (!krb5_string_to_enctype(svalue, &rparams->realm_enctype)) + rparams->realm_enctype_valid = 1; + krb5_xfree(svalue); + } + + /* Get the value for the stashfile */ + hierarchy[2] = "key_stash_file"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) + rparams->realm_stash_file = svalue; + + /* Get the value for maximum ticket lifetime. */ + hierarchy[2] = "max_life"; + if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { + rparams->realm_max_life = dtvalue; + rparams->realm_max_life_valid = 1; + } + + /* Get the value for maximum renewable ticket lifetime. */ + hierarchy[2] = "max_renewable_life"; + if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) { + rparams->realm_max_rlife = dtvalue; + rparams->realm_max_rlife_valid = 1; + } + + /* Get the value for the default principal expiration */ + hierarchy[2] = "default_principal_expiration"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + if (!krb5_string_to_timestamp(svalue, + &rparams->realm_expiration)) + rparams->realm_expiration_valid = 1; + krb5_xfree(svalue); + } + + /* Get the value for the default principal flags */ + hierarchy[2] = "default_principal_flags"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + char *sp, *ep, *tp; + + sp = svalue; + rparams->realm_flags = 0; + while (sp) { + if ((ep = strchr(sp, (int) ',')) || + (ep = strchr(sp, (int) ' ')) || + (ep = strchr(sp, (int) '\t'))) { + /* Fill in trailing whitespace of sp */ + tp = ep - 1; + while (isspace(*tp) && (tp < sp)) { + *tp = '\0'; + tp--; + } + *ep = '\0'; + ep++; + /* Skip over trailing whitespace of ep */ + while (isspace(*ep) && (*ep)) ep++; + } + /* Convert this flag */ + if (krb5_string_to_flags(sp, + "+", + "-", + &rparams->realm_flags)) + break; + sp = ep; + } + if (!sp) + rparams->realm_flags_valid = 1; + krb5_xfree(svalue); + } + + /* Get the value for the supported enctype/salttype matrix */ + hierarchy[2] = "supported_enctypes"; + if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) { + krb5_string_to_keysalts(svalue, + ", \t", /* Tuple separators */ + ":.-", /* Key/salt separators */ + 0, /* No duplicates */ + &rparams->realm_keysalts, + &rparams->realm_num_keysalts); + krb5_xfree(svalue); + } + +cleanup: + if (aprofile) + krb5_aprof_finish(aprofile); + if (lrealm) + free(lrealm); + if (kret) { + if (rparams) + krb5_free_realm_params(kcontext, rparams); + rparams = 0; + } + *rparamp = rparams; + return(kret); +} + +/* + * krb5_free_realm_params() - Free data allocated by above. + */ +krb5_error_code +krb5_free_realm_params(kcontext, rparams) + krb5_context kcontext; + krb5_realm_params *rparams; +{ + if (rparams) { + if (rparams->realm_profile) + krb5_xfree(rparams->realm_profile); + if (rparams->realm_dbname) + krb5_xfree(rparams->realm_dbname); + if (rparams->realm_mkey_name) + krb5_xfree(rparams->realm_mkey_name); + if (rparams->realm_stash_file) + krb5_xfree(rparams->realm_stash_file); + if (rparams->realm_keysalts) + krb5_xfree(rparams->realm_keysalts); + if (rparams->realm_kdc_ports) + krb5_xfree(rparams->realm_kdc_ports); + krb5_xfree(rparams); + } + return(0); +} + diff --git a/src/lib/kadm5/chpass_util.c b/src/lib/kadm5/chpass_util.c new file mode 100644 index 000000000..dbf610ce3 --- /dev/null +++ b/src/lib/kadm5/chpass_util.c @@ -0,0 +1,229 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + + +#include +#include +#include + +#include +#include "admin_internal.h" + +#include + +#define string_text error_message + +/* + * Function: kadm5_chpass_principal_util + * + * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages + * + * Arguments: + * + * princ (input) a krb5b_principal structure for the + * principal whose password we should change. + * + * new_password (input) NULL or a null terminated string with the + * the principal's desired new password. If new_password + * is NULL then this routine will read a new password. + * + * pw_ret (output) if non-NULL, points to a static buffer + * containing the new password (if password is prompted + * internally), or to the new_password argument (if + * that is non-NULL). If the former, then the buffer + * is only valid until the next call to the function, + * and the caller should be sure to zero it when + * it is no longer needed. + * + * msg_ret (output) a useful message is copied here. + * + * exit status of 0 for success, else the com err code + * for the last significant routine called. + * + * Requires: + * + * A msg_ret should point to a buffer large enough for the messasge. + * + * Effects: + * + * Modifies: + * + * + */ + +kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle, + void *lhandle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret) +{ + int code, code2, pwsize; + static char buffer[255]; + char *new_password; + kadm5_principal_ent_rec princ_ent; + kadm5_policy_ent_rec policy_ent; + + _KADM5_CHECK_HANDLE(server_handle); + + if (ret_pw) + *ret_pw = NULL; + + if (new_pw != NULL) { + new_password = new_pw; + } else { /* read the password */ + krb5_context context; + + if ((code = (int) krb5_init_context(&context)) == 0) { + pwsize = sizeof(buffer); + code = krb5_read_password(context, KADM5_PW_FIRST_PROMPT, + KADM5_PW_SECOND_PROMPT, + buffer, &pwsize); + krb5_free_context(context); + } + + if (code == 0) + new_password = buffer; + else { +#ifdef ZEROPASSWD + memset(buffer, 0, sizeof(buffer)); +#endif + if (code == KRB5_LIBOS_BADPWDMATCH) { + strcpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH)); + return(code); + } else { + sprintf(msg_ret, "%s %s\n%s\n", error_message(code), + string_text(CHPASS_UTIL_WHILE_READING_PASSWORD), + string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); + return(code); + } + } + if (pwsize == 0) { +#ifdef ZEROPASSWD + memset(buffer, 0, sizeof(buffer)); +#endif + strcpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ)); + return(KRB5_LIBOS_CANTREADPWD); /* could do better */ + } + } + + if (ret_pw) + *ret_pw = new_password; + + code = kadm5_chpass_principal(server_handle, princ, new_password); + +#ifdef ZEROPASSWD + if (!ret_pw) + memset(buffer, 0, sizeof(buffer)); /* in case we read a new password */ +#endif + + if (code == KADM5_OK) { + strcpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED)); + return(0); + } + + if ((code != KADM5_PASS_Q_TOOSHORT) && + (code != KADM5_PASS_REUSE) &&(code != KADM5_PASS_Q_CLASS) && + (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) { + /* Can't get more info for other errors */ + sprintf(buffer, "%s %s", error_message(code), + string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); + sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), + buffer); + return(code); + } + + /* Ok, we have a password quality error. Return a good message */ + + if (code == KADM5_PASS_REUSE) { + strcpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE)); + return(code); + } + + if (code == KADM5_PASS_Q_DICT) { + strcpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY)); + return(code); + } + + /* Look up policy for the remaining messages */ + + code2 = kadm5_get_principal (lhandle, princ, &princ_ent, + KADM5_PRINCIPAL_NORMAL_MASK); + if (code2 != 0) { + sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2), + string_text(CHPASS_UTIL_GET_PRINC_INFO), + error_message(code), + string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), + string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); + return(code); + } + + if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) { + sprintf(msg_ret, "%s %s\n\n%s", error_message(code), + string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR), + string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + return(code); + } + + code2 = kadm5_get_policy(lhandle, princ_ent.policy, + &policy_ent); + if (code2 != 0) { + sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2), + string_text(CHPASS_UTIL_GET_POLICY_INFO), + error_message(code), + string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE), + string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED)); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + return(code); + } + + if (code == KADM5_PASS_Q_TOOSHORT) { + sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT), + policy_ent.pw_min_length); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + (void) kadm5_free_policy_ent(lhandle, &policy_ent); + return(code); + } + +/* Can't get more info for other errors */ + + if (code == KADM5_PASS_Q_CLASS) { + sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES), + policy_ent.pw_min_classes); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + (void) kadm5_free_policy_ent(lhandle, &policy_ent); + return(code); + } + + if (code == KADM5_PASS_TOOSOON) { + time_t until; + char *time_string, *ptr; + + until = princ_ent.last_pwd_change + policy_ent.pw_min_life; + + time_string = ctime(&until); + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + (void) kadm5_free_policy_ent(lhandle, &policy_ent); + return(code); + } + + /* We should never get here, but just in case ... */ + sprintf(buffer, "%s %s", error_message(code), + string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE)); + sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), + buffer); + (void) kadm5_free_principal_ent(lhandle, &princ_ent); + (void) kadm5_free_policy_ent(lhandle, &policy_ent); + return(code); +} diff --git a/src/lib/kadm5/chpass_util_strings.et b/src/lib/kadm5/chpass_util_strings.et new file mode 100644 index 000000000..032254998 --- /dev/null +++ b/src/lib/kadm5/chpass_util_strings.et @@ -0,0 +1,58 @@ +# this is really a string table for ovsec_kadm_chpass_principal_util + +error_table ovku + +error_code CHPASS_UTIL_GET_POLICY_INFO, "while getting policy info." +error_code CHPASS_UTIL_GET_PRINC_INFO, "while getting principal info." +error_code CHPASS_UTIL_NEW_PASSWORD_MISMATCH, + "New passwords do not match - password not changed.\n" + +error_code CHPASS_UTIL_NEW_PASSWORD_PROMPT, "New password:" +error_code CHPASS_UTIL_NEW_PASSWORD_AGAIN_PROMPT, "New password (again):" +error_code CHPASS_UTIL_NO_PASSWORD_READ, "You must type a password. Passwords must be at least one character long.\n" +error_code CHPASS_UTIL_NO_POLICY_YET_Q_ERROR, +"yet no policy set! Contact your system security administrator." + +error_code CHPASS_UTIL_PASSWORD_CHANGED, "Password changed.\n" + +error_code CHPASS_UTIL_PASSWORD_IN_DICTIONARY, +"New password was found in a dictionary of possible passwords and\n\ +therefore may be easily guessed. Please choose another password.\n\ +See the ovpasswd man page for help in choosing a good password." + +error_code CHPASS_UTIL_PASSWORD_NOT_CHANGED, "Password not changed." + +error_code CHPASS_UTIL_PASSWORD_TOO_SHORT, +"New password is too short.\n\ +Please choose a password which is at least %d characters long." +# /* */ + +error_code CHPASS_UTIL_TOO_FEW_CLASSES, +"New password does not have enough character classes.\n\ +The character classes are:\n\ + - lower-case letters,\n\ + - upper-case letters,\n\ + - digits,\n\ + - punctuation, and\n\ + - all other characters (e.g., control characters).\n\ +Please choose a password with at least %d character classes." +# /* */ + + +error_code CHPASS_UTIL_PASSWORD_TOO_SOON, +"Password cannot be changed because it was changed too recently.\n\ +Please wait until %s before you change it.\n\ +If you need to change your password before then, contact your system\n\ +security administrator." +# /* */ + +error_code CHPASS_UTIL_PASSWORD_REUSE, +"New password was used previously. Please choose a different password." + +error_code CHPASS_UTIL_WHILE_TRYING_TO_CHANGE, +"while trying to change password." + +error_code CHPASS_UTIL_WHILE_READING_PASSWORD, "while reading new password." + +end + diff --git a/src/lib/kadm5/client_handle.c b/src/lib/kadm5/client_handle.c new file mode 100644 index 000000000..895777a6e --- /dev/null +++ b/src/lib/kadm5/client_handle.c @@ -0,0 +1,9 @@ +#include +#include +#include "client_internal.h" + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/client_init.c b/src/lib/kadm5/client_init.c new file mode 100644 index 000000000..bfc1c3a3c --- /dev/null +++ b/src/lib/kadm5/client_init.c @@ -0,0 +1,554 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for KRB5_ADM_DEFAULT_PORT */ +#ifdef __STDC__ +#include +#endif + +#include +#include +#include "client_internal.h" + +#include +#include +#include +#include + +#define ADM_CCACHE "/tmp/ovsec_adm.XXXXXX" + +#ifndef _POSIX_SOURCE /* Perhaps this should actually say __STDC__ */ +int setenv(const char *var, const char *val, int flag); +void unsetenv(const char *var); +#endif + +enum init_type { INIT_PASS, INIT_SKEY, INIT_CREDS }; + +static kadm5_ret_t _kadm5_init_any(char *client_name, + enum init_type init_type, + char *pass, + krb5_ccache ccache_in, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle); + +kadm5_ret_t kadm5_init_with_creds(char *client_name, + krb5_ccache ccache, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_CREDS, NULL, ccache, + service_name, params, + struct_version, api_version, + server_handle); +} + + +kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +kadm5_ret_t kadm5_init(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_PASS, pass, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return _kadm5_init_any(client_name, INIT_SKEY, keytab, NULL, + service_name, params, struct_version, + api_version, server_handle); +} + +/* + * Try no preauthentication first; then try the encrypted timestamp + * (stolen from krb5 kinit.c) + */ +static int preauth_search_list[] = { + 0, + KRB5_PADATA_ENC_UNIX_TIME, + -1 +}; + +static kadm5_ret_t _kadm5_init_any(char *client_name, + enum init_type init_type, + char *pass, + krb5_ccache ccache_in, + char *service_name, + kadm5_config_params *params_in, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + struct sockaddr_in addr; + struct hostent *hp; + struct servent *srv; + int fd; + int i; + + char full_service_name[BUFSIZ], host[MAXHOSTNAMELEN], *ccname_orig; + char *realm; + krb5_creds creds; + krb5_ccache ccache = NULL; + krb5_timestamp now; + + OM_uint32 gssstat, minor_stat; + gss_buffer_desc input_name; + gss_name_t gss_target, gss_client; + gss_cred_id_t gss_client_creds = GSS_C_NO_CREDENTIAL; + + kadm5_server_handle_t handle; + kadm5_config_params params_local; + + int code = 0; + generic_ret *r; + + initialize_ovk_error_table(); + initialize_adb_error_table(); + initialize_ovku_error_table(); + + if (! server_handle) { + return EINVAL; + } + + if (! (handle = malloc(sizeof(*handle)))) { + return ENOMEM; + } + if (! (handle->lhandle = malloc(sizeof(*handle)))) { + free(handle); + return ENOMEM; + } + + handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; + handle->struct_version = struct_version; + handle->api_version = api_version; + handle->clnt = 0; + handle->cache_name = 0; + handle->destroy_cache = 0; + *handle->lhandle = *handle; + handle->lhandle->api_version = KADM5_API_VERSION_2; + handle->lhandle->struct_version = KADM5_STRUCT_VERSION; + handle->lhandle->lhandle = handle->lhandle; + + krb5_init_context(&handle->context); + krb5_init_ets(handle->context); + + if(service_name == NULL || client_name == NULL) { + free(handle); + return EINVAL; + } + memset((char *) &creds, 0, sizeof(creds)); + + /* + * Verify the version numbers before proceeding; we can't use + * CHECK_HANDLE because not all fields are set yet. + */ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, + KADM5_NEW_LIB_API_VERSION); + + /* + * Acquire relevant profile entries. In version 2, merge values + * in params_in with values from profile, based on + * params_in->mask. + * + * In version 1, we've given a realm (which may be NULL) instead + * of params_in. So use that realm, make params_in contain an + * empty mask, and behave like version 2. + */ + memset((char *) ¶ms_local, 0, sizeof(params_local)); + if (api_version == KADM5_API_VERSION_1) { + realm = params_local.realm = (char *) params_in; + if (params_in) + params_local.mask = KADM5_CONFIG_REALM; + params_in = ¶ms_local; + } else { + if (params_in && (params_in->mask & KADM5_CONFIG_REALM)) + realm = params_in->realm; + else + realm = NULL; + } + +#define ILLEGAL_PARAMS (KADM5_CONFIG_DBNAME | KADM5_CONFIG_ADBNAME | \ + KADM5_CONFIG_ADB_LOCKFILE | \ + KADM5_CONFIG_ACL_FILE | KADM5_CONFIG_DICT_FILE \ + | KADM5_CONFIG_ADMIN_KEYTAB | \ + KADM5_CONFIG_STASH_FILE | \ + KADM5_CONFIG_MKEY_NAME | KADM5_CONFIG_ENCTYPE \ + | KADM5_CONFIG_MAX_LIFE | \ + KADM5_CONFIG_MAX_RLIFE | \ + KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_FLAGS | \ + KADM5_CONFIG_ENCTYPES | KADM5_CONFIG_MKEY_FROM_KBD) + + if (params_in && params_in->mask & ILLEGAL_PARAMS) { + free(handle); + return KADM5_BAD_CLIENT_PARAMS; + } + + if (code = kadm5_get_config_params(handle->context, + "/etc/krb5.conf", + "KRB5_CONFIG", + params_in, + &handle->params)) { + krb5_free_context(handle->context); + free(handle); + return(code); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | \ + KADM5_CONFIG_ADMIN_SERVER | \ + KADM5_CONFIG_KADMIND_PORT) + + if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_free_context(handle->context); + free(handle); + return KRB5_CONFIG_BADFORMAT; + } + + /* + * Acquire a service ticket for service_name@realm in the name of + * client_name, using password pass (which could be NULL), and + * create a ccache to store them in. If INIT_CREDS, use the + * ccache we were provided instead. + */ + + if ((code = krb5_parse_name(handle->context, client_name, &creds.client))) + goto error; + + if (realm) { + sprintf(full_service_name, "%s@%s", service_name, realm); + } else { + /* krb5_princ_realm(creds.client) is not null terminated */ + strcpy(full_service_name, service_name); + strcat(full_service_name, "@"); + strncat(full_service_name, krb5_princ_realm(handle->context, + creds.client)->data, + krb5_princ_realm(handle->context, creds.client)->length); + } + + if ((code = krb5_parse_name(handle->context, full_service_name, + &creds.server))) + goto error; + + /* XXX temporarily fix a bug in krb5_cc_get_type */ +#undef krb5_cc_get_type +#define krb5_cc_get_type(context, cache) ((cache)->ops->prefix) + + + if (init_type == INIT_CREDS) { + ccache = ccache_in; + handle->cache_name = (char *) + malloc(strlen(krb5_cc_get_type(handle->context, ccache)) + + strlen(krb5_cc_get_name(handle->context, ccache)) + 2); + if (handle->cache_name == NULL) { + code = ENOMEM; + goto error; + } + sprintf(handle->cache_name, "%s:%s", + krb5_cc_get_type(handle->context, ccache), + krb5_cc_get_name(handle->context, ccache)); + } else { + handle->cache_name = + (char *) malloc(strlen(ADM_CCACHE)+strlen("FILE:")+1); + if (handle->cache_name == NULL) { + code = ENOMEM; + goto error; + } + sprintf(handle->cache_name, "FILE:%s", ADM_CCACHE); + mktemp(handle->cache_name + strlen("FILE:")); + + if ((code = krb5_cc_resolve(handle->context, handle->cache_name, + &ccache))) + goto error; + + if ((code = krb5_cc_initialize (handle->context, ccache, + creds.client))) + goto error; + + handle->destroy_cache = 1; + } + handle->lhandle->cache_name = handle->cache_name; + + if ((code = krb5_timeofday(handle->context, &now))) + goto error; + + /* + * Get a ticket, use the method specified in init_type. + */ + + creds.times.starttime = 0; /* start timer at KDC */ + creds.times.endtime = 0; /* endtime will be limited by service */ + + if (init_type == INIT_PASS) { + for (i=0; preauth_search_list[i] >= 0; i++) { + code = krb5_get_in_tkt_with_password(handle->context, + 0, /* no options */ + 0, /* default addresses */ + NULL, + NULL, /* XXX preauth */ + pass, + ccache, + &creds, + NULL); + if (code != KRB5KDC_ERR_PREAUTH_FAILED && + code != KRB5KDC_ERR_PREAUTH_REQUIRED && + code != KRB5KRB_ERR_GENERIC) + break; + } + } else if (init_type == INIT_SKEY) { + krb5_keytab kt = NULL; + + if (pass && (code = krb5_kt_resolve(handle->context, pass, &kt))) + ; + else { + for (i=0; preauth_search_list[i] >= 0; i++) { + code = krb5_get_in_tkt_with_keytab(handle->context, + 0, /* no options */ + 0, /* default addrs */ + NULL, + NULL, /* XXX preauth */ + kt, + ccache, + &creds, + NULL); + if (code != KRB5KDC_ERR_PREAUTH_FAILED && + code != KRB5KDC_ERR_PREAUTH_REQUIRED && + code != KRB5KRB_ERR_GENERIC) + break; + } + + if (pass) krb5_kt_close(handle->context, kt); + } + } + + /* Improved error messages */ + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD; + if (code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) + code = KADM5_SECURE_PRINC_MISSING; + + if (code != 0) goto error; + +#ifdef ZEROPASSWD + if (pass != NULL) + memset(pass, 0, strlen(pass)); +#endif + + /* + * We have ticket; open the RPC connection. + */ + + hp = gethostbyname(handle->params.admin_server); + if (hp == (struct hostent *) NULL) { + code = KRB5_CONFIG_BADFORMAT; + goto cleanup; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = hp->h_addrtype; + (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr, + sizeof(addr.sin_addr)); + addr.sin_port = htons((u_short) handle->params.kadmind_port); + + fd = RPC_ANYSOCK; + + handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0); + if (handle->clnt == NULL) { + code = KADM5_RPC_ERROR; + goto error; + } + handle->lhandle->clnt = handle->clnt; + + /* now that handle->clnt is set, we can check the handle */ + if (code = _kadm5_check_handle((void *) handle)) + goto error; + + /* + * The RPC connection is open; establish the GSS-API + * authentication context. + */ + + /* use the kadm5 cache */ + ccname_orig = getenv("KRB5CCNAME"); + if (ccname_orig) + ccname_orig = strdup(ccname_orig); + + (void) setenv("KRB5CCNAME", handle->cache_name, 1); + +#ifndef INIT_TEST + input_name.value = full_service_name; + input_name.length = strlen((char *)input_name.value) + 1; + gssstat = gss_import_name(&minor_stat, &input_name, + gss_nt_krb5_name, &gss_target); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } +#endif /* ! INIT_TEST */ + + input_name.value = client_name; + input_name.length = strlen((char *)input_name.value) + 1; + gssstat = gss_import_name(&minor_stat, &input_name, + gss_nt_krb5_name, &gss_client); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } + + gssstat = gss_acquire_cred(&minor_stat, gss_client, 0, + GSS_C_NULL_OID_SET, GSS_C_INITIATE, + &gss_client_creds, NULL, NULL); + (void) gss_release_name(&minor_stat, &gss_client); + if (gssstat != GSS_S_COMPLETE) { + code = KADM5_GSS_ERROR; + goto error; + } + +#ifndef INIT_TEST + handle->clnt->cl_auth = auth_gssapi_create(handle->clnt, + &gssstat, + &minor_stat, + gss_client_creds, + gss_target, + GSS_C_NULL_OID, + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, + 0, + NULL, + NULL, + NULL); + (void) gss_release_name(&minor_stat, &gss_target); +#endif /* ! INIT_TEST */ + + if (ccname_orig) { + (void) setenv("KRB5CCNAME", ccname_orig, 1); + free(ccname_orig); + } else + (void) unsetenv("KRB5CCNAME"); + + + if (handle->clnt->cl_auth == NULL) { + code = KADM5_GSS_ERROR; + goto error; + } + + r = init_1(&handle->api_version, handle->clnt); + if (r == NULL) { + code = KADM5_RPC_ERROR; + goto error; + } + if (r->code) { + code = r->code; + goto error; + } + + *server_handle = (void *) handle; + + if (init_type != INIT_CREDS) + krb5_cc_close(handle->context, ccache); + + goto cleanup; + +error: + /* + * Note that it is illegal for this code to execute if "handle" + * has not been allocated and initialized. I.e., don't use "goto + * error" before the block of code at the top of the function + * that allocates and initializes "handle". + */ + if (handle->cache_name) + free(handle->cache_name); + if (handle->destroy_cache && ccache) + krb5_cc_destroy(handle->context, ccache); + if(handle->clnt && handle->clnt->cl_auth) + AUTH_DESTROY(handle->clnt->cl_auth); + if(handle->clnt) + clnt_destroy(handle->clnt); + +cleanup: + krb5_free_cred_contents(handle->context, &creds); + if (gss_client_creds != GSS_C_NO_CREDENTIAL) + (void) gss_release_cred(&minor_stat, &gss_client_creds); + + if (code) + free(handle); + + return code; +} + +kadm5_ret_t +kadm5_destroy(void *server_handle) +{ + krb5_ccache ccache = NULL; + int code = KADM5_OK; + kadm5_server_handle_t handle = + (kadm5_server_handle_t) server_handle; + + CHECK_HANDLE(server_handle); + + if (handle->destroy_cache && handle->cache_name) { + if ((code = krb5_cc_resolve(handle->context, + handle->cache_name, &ccache)) == 0) + code = krb5_cc_destroy (handle->context, ccache); + } + if (handle->cache_name) + free(handle->cache_name); + if (handle->clnt && handle->clnt->cl_auth) + AUTH_DESTROY(handle->clnt->cl_auth); + if (handle->clnt) + clnt_destroy(handle->clnt); + + handle->magic_number = 0; + free(handle); + + return code; +} + +kadm5_ret_t kadm5_flush(void *server_handle) +{ + return KADM5_OK; +} + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/client_internal.h b/src/lib/kadm5/client_internal.h new file mode 100644 index 000000000..5a246158e --- /dev/null +++ b/src/lib/kadm5/client_internal.h @@ -0,0 +1,93 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.11 1996/07/22 20:35:46 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.10.4.1 1996/07/18 03:08:37 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.10.2.1 1996/06/20 02:16:46 marc + * File added to the repository on a branch + * + * Revision 1.10 1996/06/06 20:09:16 bjaspan + * add destroy_cache, for kadm5_init_with_creds + * + * Revision 1.9 1996/05/30 21:04:42 bjaspan + * add lhandle to handle + * + * Revision 1.8 1996/05/28 20:33:49 bjaspan + * rework kadm5_config + * + * Revision 1.7 1996/05/17 21:36:59 bjaspan + * rename to kadm5, begin implementing version 2 + * + * Revision 1.6 1996/05/16 21:45:07 bjaspan + * add context + * + * Revision 1.5 1996/05/08 21:10:23 bjaspan + * marc's changes + * + * Revision 1.4 1996/01/16 20:54:30 grier + * secure/3570 use krb5_ui_4 not unsigned int + * + * Revision 1.3 1995/11/14 17:48:57 grier + * long to int + * + * Revision 1.2 1994/08/16 18:53:47 jik + * Versioning stuff. + * + * Revision 1.1 1994/08/09 21:14:38 jik + * Initial revision + * + */ + +/* + * This header file is used internally by the Admin API client + * libraries. IF YOU THINK YOU NEED TO USE THIS FILE FOR ANYTHING, + * YOU'RE ALMOST CERTAINLY WRONG. + */ + +#ifndef __KADM5_CLIENT_INTERNAL_H__ +#define __KADM5_CLIENT_INTERNAL_H__ + +#include "admin_internal.h" + +typedef struct _kadm5_server_handle_t { + krb5_ui_4 magic_number; + krb5_ui_4 struct_version; + krb5_ui_4 api_version; + char * cache_name; + int destroy_cache; + CLIENT * clnt; + krb5_context context; + kadm5_config_params params; + struct _kadm5_server_handle_t *lhandle; +} kadm5_server_handle_rec, *kadm5_server_handle_t; + +#define CLIENT_CHECK_HANDLE(handle) \ +{ \ + kadm5_server_handle_t srvr = \ + (kadm5_server_handle_t) handle; \ + \ + if (! srvr->clnt) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (! srvr->cache_name) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (! srvr->lhandle) \ + return KADM5_BAD_SERVER_HANDLE; \ +} + +#define CHECK_HANDLE(handle) \ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_LIB_API_VERSION, \ + KADM5_NEW_LIB_API_VERSION) \ + CLIENT_CHECK_HANDLE(handle) + +#endif /* __KADM5_CLIENT_INTERNAL_H__ */ diff --git a/src/lib/kadm5/client_principal.c b/src/lib/kadm5/client_principal.c new file mode 100644 index 000000000..c41922722 --- /dev/null +++ b/src/lib/kadm5/client_principal.c @@ -0,0 +1,307 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include "client_internal.h" + +kadm5_ret_t +kadm5_create_principal(void *server_handle, + kadm5_principal_ent_t princ, long mask, + char *pw) +{ + generic_ret *r; + cprinc_arg arg; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.mask = mask; + arg.passwd = pw; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); + if (handle->api_version == KADM5_API_VERSION_1) { + /* + * hack hack cough cough. + * krb5_unparse name dumps core if we pass it in garbage + * or null. So, since the client is not allowed to set mod_name + * anyway, we just fill it in with a dummy principal. The server of + * course ignores this. + */ + krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); + } else + arg.rec.mod_name = NULL; + + if(!(mask & KADM5_POLICY)) + arg.rec.policy = NULL; + if (! (mask & KADM5_KEY_DATA)) { + arg.rec.n_key_data = 0; + arg.rec.key_data = NULL; + } + if (! (mask & KADM5_TL_DATA)) { + arg.rec.n_tl_data = 0; + arg.rec.tl_data = NULL; + } + + r = create_principal_1(&arg, handle->clnt); + + if (handle->api_version == KADM5_API_VERSION_1) + krb5_free_principal(handle->context, arg.rec.mod_name); + + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_delete_principal(void *server_handle, krb5_principal principal) +{ + dprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(principal == NULL) + return EINVAL; + arg.princ = principal; + arg.api_version = handle->api_version; + r = delete_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_modify_principal(void *server_handle, + kadm5_principal_ent_t princ, long mask) +{ + mprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.mask = mask; + arg.api_version = handle->api_version; + /* + * cough cough gag gag + * see comment in create_principal. + */ + if(princ == NULL) + return EINVAL; + memcpy(&arg.rec, princ, sizeof(kadm5_principal_ent_rec)); + if(!(mask & KADM5_POLICY)) + arg.rec.policy = NULL; + if (! (mask & KADM5_KEY_DATA)) { + arg.rec.n_key_data = 0; + arg.rec.key_data = NULL; + } + if (! (mask & KADM5_TL_DATA)) { + arg.rec.n_tl_data = 0; + arg.rec.tl_data = NULL; + } + + if (handle->api_version == KADM5_API_VERSION_1) { + /* + * See comment in create_principal + */ + krb5_parse_name(handle->context, "bogus/bogus", &arg.rec.mod_name); + } else + arg.rec.mod_name = NULL; + + r = modify_principal_1(&arg, handle->clnt); + + if (handle->api_version == KADM5_API_VERSION_1) + krb5_free_principal(handle->context, arg.rec.mod_name); + + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_get_principal(void *server_handle, + krb5_principal princ, kadm5_principal_ent_t ent, + long mask) +{ + gprinc_arg arg; + gprinc_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(princ == NULL) + return EINVAL; + arg.princ = princ; + if (handle->api_version == KADM5_API_VERSION_1) + arg.mask = KADM5_PRINCIPAL_NORMAL_MASK; + else + arg.mask = mask; + arg.api_version = handle->api_version; + r = get_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + kadm5_principal_ent_t_v1 *entp; + + entp = (kadm5_principal_ent_t_v1 *) ent; + if (r->code == 0) { + if (!(*entp = (kadm5_principal_ent_t_v1) + malloc(sizeof(kadm5_principal_ent_rec_v1)))) + return ENOMEM; + /* this memcpy works because the v1 structure is an initial + subset of the v2 struct. C guarantees that this will + result in the same layout in memory */ + memcpy(*entp, &r->rec, sizeof(**entp)); + } else { + *entp = NULL; + } + } else { + if (r->code == 0) + memcpy(ent, &r->rec, sizeof(r->rec)); + } + + return r->code; +} + +kadm5_ret_t +kadm5_get_principals(void *server_handle, + char *exp, char ***princs, int *count) +{ + gprincs_arg arg; + gprincs_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(princs == NULL || count == NULL) + return EINVAL; + arg.exp = exp; + arg.api_version = handle->api_version; + r = get_princs_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if(r->code == 0) { + *count = r->count; + *princs = r->princs; + } else { + *count = 0; + *princs = NULL; + } + + return r->code; +} + +kadm5_ret_t +kadm5_rename_principal(void *server_handle, + krb5_principal source, krb5_principal dest) +{ + rprinc_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.src = source; + arg.dest = dest; + arg.api_version = handle->api_version; + if (source == NULL || dest == NULL) + return EINVAL; + r = rename_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_chpass_principal(void *server_handle, + krb5_principal princ, char *password) +{ + chpass_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.princ = princ; + arg.pass = password; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + r = chpass_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_randkey_principal(void *server_handle, + krb5_principal princ, + krb5_keyblock **key, int *n_keys) +{ + chrand_arg arg; + chrand_ret *r; + krb5_keyblock new; + kadm5_server_handle_t handle = server_handle; + int i, ret; + + CHECK_HANDLE(server_handle); + + arg.princ = princ; + arg.api_version = handle->api_version; + + if(princ == NULL) + return EINVAL; + r = chrand_principal_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + if (key) + krb5_copy_keyblock(handle->context, &r->key, key); + } else { + if (n_keys) + *n_keys = r->n_keys; + if (key) { + *key = (krb5_keyblock *) malloc(r->n_keys*sizeof(krb5_keyblock)); + if (*key == NULL) + return ENOMEM; + for (i = 0; i < r->n_keys; i++) { + ret = krb5_copy_keyblock_contents(handle->context, + &r->keys[i], + &(*key)[i]); + if (ret) { + free(*key); + return ENOMEM; + } + } + } + } + + return r->code; +} + +/* not supported on client side */ +kadm5_ret_t kadm5_decrypt_key(void *server_handle, + kadm5_principal_ent_t entry, krb5_int32 + ktype, krb5_int32 stype, krb5_int32 + kvno, krb5_keyblock *keyblock, + krb5_keysalt *keysalt, int *kvnop) +{ + return EINVAL; +} diff --git a/src/lib/kadm5/client_rpc.c b/src/lib/kadm5/client_rpc.c new file mode 100644 index 000000000..547844a2c --- /dev/null +++ b/src/lib/kadm5/client_rpc.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include + +/* Default timeout can be changed using clnt_control() */ +static struct timeval TIMEOUT = { 25, 0 }; + +generic_ret * +create_principal_1(argp, clnt) + cprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CREATE_PRINCIPAL, xdr_cprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +delete_principal_1(argp, clnt) + dprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, DELETE_PRINCIPAL, xdr_dprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +modify_principal_1(argp, clnt) + mprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, MODIFY_PRINCIPAL, xdr_mprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +rename_principal_1(argp, clnt) + rprinc_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, RENAME_PRINCIPAL, xdr_rprinc_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gprinc_ret * +get_principal_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gprinc_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRINCIPAL, xdr_gprinc_arg, argp, xdr_gprinc_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gprincs_ret * +get_princs_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gprincs_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRINCS, xdr_gprincs_arg, argp, + xdr_gprincs_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +chpass_principal_1(argp, clnt) + chpass_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CHPASS_PRINCIPAL, xdr_chpass_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +chrand_ret * +chrand_principal_1(argp, clnt) + chrand_arg *argp; + CLIENT *clnt; +{ + static chrand_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CHRAND_PRINCIPAL, xdr_chrand_arg, argp, xdr_chrand_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +create_policy_1(argp, clnt) + cpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, CREATE_POLICY, xdr_cpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +delete_policy_1(argp, clnt) + dpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, DELETE_POLICY, xdr_dpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +modify_policy_1(argp, clnt) + mpol_arg *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, MODIFY_POLICY, xdr_mpol_arg, argp, xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gpol_ret * +get_policy_1(argp, clnt) + gpol_arg *argp; + CLIENT *clnt; +{ + static gpol_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_POLICY, xdr_gpol_arg, argp, xdr_gpol_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +gpols_ret * +get_pols_1(argp, clnt) + gprinc_arg *argp; + CLIENT *clnt; +{ + static gpols_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_POLS, xdr_gpols_arg, argp, + xdr_gpols_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +getprivs_ret *get_privs_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + static getprivs_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, GET_PRIVS, xdr_u_int32, argp, + xdr_getprivs_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} + +generic_ret * +init_1(argp, clnt) + void *argp; + CLIENT *clnt; +{ + static generic_ret res; + + memset((char *)&res, 0, sizeof(res)); + if (clnt_call(clnt, INIT, xdr_u_int32, argp, + xdr_generic_ret, &res, TIMEOUT) != RPC_SUCCESS) { + return (NULL); + } + return (&res); +} diff --git a/src/lib/kadm5/clnt_chpass_util.c b/src/lib/kadm5/clnt_chpass_util.c new file mode 100644 index 000000000..d6c7f0bfb --- /dev/null +++ b/src/lib/kadm5/clnt_chpass_util.c @@ -0,0 +1,15 @@ +#include +#include "client_internal.h" + +kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + return _kadm5_chpass_principal_util(handle, handle->lhandle, princ, + new_pw, ret_pw, msg_ret); +} diff --git a/src/lib/kadm5/clnt_policy.c b/src/lib/kadm5/clnt_policy.c new file mode 100644 index 000000000..f81cf7471 --- /dev/null +++ b/src/lib/kadm5/clnt_policy.c @@ -0,0 +1,151 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "client_internal.h" +#include +#include + +kadm5_ret_t +kadm5_create_policy(void *server_handle, + kadm5_policy_ent_t policy, long mask) +{ + cpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(policy == (kadm5_policy_ent_t) NULL) + return EINVAL; + + arg.mask = mask; + arg.api_version = handle->api_version; + memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); + r = create_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_delete_policy(void *server_handle, char *name) +{ + dpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(name == NULL) + return EINVAL; + + arg.name = name; + arg.api_version = handle->api_version; + + r = delete_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_modify_policy(void *server_handle, + kadm5_policy_ent_t policy, long mask) + +{ + mpol_arg arg; + generic_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(policy == (kadm5_policy_ent_t) NULL) + return EINVAL; + + arg.mask = mask; + arg.api_version = handle->api_version; + + memcpy(&arg.rec, policy, sizeof(kadm5_policy_ent_rec)); + r = modify_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + return r->code; +} + +kadm5_ret_t +kadm5_get_policy(void *server_handle, char *name, kadm5_policy_ent_t ent) + +{ + gpol_arg arg; + gpol_ret *r; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + arg.name = name; + arg.api_version = handle->api_version; + + if(name == NULL) + return EINVAL; + + r = get_policy_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if (handle->api_version == KADM5_API_VERSION_1) { + kadm5_policy_ent_t *entp; + + entp = (kadm5_policy_ent_t *) ent; + if(r->code == 0) { + if (!(*entp = (kadm5_policy_ent_t) + malloc(sizeof(kadm5_policy_ent_rec)))) + return ENOMEM; + memcpy(*entp, &r->rec, sizeof(**entp)); + } else { + *entp = NULL; + } + } else { + if (r->code == 0) + memcpy(ent, &r->rec, sizeof(r->rec)); + } + + return r->code; +} + +kadm5_ret_t +kadm5_get_policies(void *server_handle, + char *exp, char ***pols, int *count) +{ + gpols_arg arg; + gpols_ret *r; + kadm5_server_handle_t handle = server_handle; + krb5_error_code retval; + + CHECK_HANDLE(server_handle); + + if(pols == NULL || count == NULL) + return EINVAL; + arg.exp = exp; + arg.api_version = handle->api_version; + r = get_pols_1(&arg, handle->clnt); + if(r == NULL) + return KADM5_RPC_ERROR; + if(r->code == 0) { + *count = r->count; + *pols = r->pols; + } else { + *count = 0; + *pols = NULL; + } + + return r->code; +} diff --git a/src/lib/kadm5/clnt_privs.c b/src/lib/kadm5/clnt_privs.c new file mode 100644 index 000000000..93ea199e2 --- /dev/null +++ b/src/lib/kadm5/clnt_privs.c @@ -0,0 +1,66 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.6 1996/07/22 20:35:57 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.5.4.1 1996/07/18 03:08:45 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.5.2.1 1996/06/20 02:16:53 marc + * File added to the repository on a branch + * + * Revision 1.5 1996/05/17 21:36:50 bjaspan + * rename to kadm5, begin implementing version 2 + * + * Revision 1.4 1996/05/16 21:45:51 bjaspan + * u_int32 -> long, add krb5_context + * + * Revision 1.3 1994/09/20 16:25:05 bjaspan + * [secure-admin/2436: API versioning fixes to various admin files] + * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes] + * + * Sandbox: + * + * Unnecessary variable initialization removed. + * + * Revision 1.3 1994/09/12 20:26:39 jik + * Unnecessary variable initialization removed. + * + * Revision 1.2 1994/08/16 18:52:02 jik + * Versioning changes. + * + * Revision 1.1 1993/11/10 23:10:39 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "client_internal.h" + +kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) +{ + getprivs_ret *r; + kadm5_server_handle_t handle = server_handle; + + r = get_privs_1(&handle->api_version, handle->clnt); + if (r == NULL) + return KADM5_RPC_ERROR; + else if (r->code == KADM5_OK) + *privs = r->privs; + return r->code; +} diff --git a/src/lib/kadm5/configure.in b/src/lib/kadm5/configure.in new file mode 100644 index 000000000..6466673a8 --- /dev/null +++ b/src/lib/kadm5/configure.in @@ -0,0 +1,49 @@ +AC_INIT(configure.in) +CONFIG_RULES +AC_PROG_ARCHIVE +AC_PROG_ARCHIVE_ADD +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PROG_LEX +AC_PROG_AWK +AC_CHECK_HEADERS(syslog.h) +AC_REPLACE_FUNCS(setenv) +save_LIBS="$LIBS" +LIBS=-lgen +AC_CHECK_FUNCS(compile step) +[if test "$krb5_cv_func_compile" = true ; then + LIBS="$save_LIBS -lgen" +else + LIBS="$save_LIBS" +fi] +AC_CHECK_FUNCS(re_comp re_exec regcomp regexec openlog syslog closelog strftime vsprintf) +V5_SHARED_LIB_OBJS +V5_MAKE_SHARED_LIB(libkadm5srv,1.0,.., ./kadm5,srv) +V5_MAKE_SHARED_LIB(libkadm5clnt,1.0,.., ./kadm5,clnt) +GSSRPC_SH_VERS=$krb5_cv_shlib_version_libgssrpc +AC_SUBST(GSSRPC_SH_VERS) +GSSAPI_KRB5_SH_VERS=$krb5_cv_shlib_version_libgssapi_krb5 +AC_SUBST(GSSAPI_KRB5_SH_VERS) +KDB5_SH_VERS=$krb5_cv_shlib_version_libkdb5 +AC_SUBST(KDB5_SH_VERS) +KRB5_SH_VERS=$krb5_cv_shlib_version_libkrb5 +AC_SUBST(KRB5_SH_VERS) +CRYPTO_SH_VERS=$krb5_cv_shlib_version_libcrypto +AC_SUBST(CRYPTO_SH_VERS) +COMERR_SH_VERS=$krb5_cv_shlib_version_libcom_err +AC_SUBST(COMERR_SH_VERS) +DYN_SH_VERS=$krb5_cv_shlib_version_libdyn +AC_SUBST(DYN_SH_VERS) +CopySrcHeader(adb.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(admin.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(admin_internal.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(admin_xdr.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(client_internal.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(kadm_rpc.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(server_acl.h,[$](BUILDTOP)/include/kadm5) +CopySrcHeader(server_internal.h,[$](BUILDTOP)/include/kadm5) +CopyHeader(adb_err.h,[$](BUILDTOP)/include/kadm5) +CopyHeader(chpass_util_strings.h,[$](BUILDTOP)/include/kadm5) +CopyHeader(kadm_err.h,[$](BUILDTOP)/include/kadm5) +AppendRule([all:: all-$(WHAT)]) +V5_AC_OUTPUT_MAKEFILE diff --git a/src/lib/kadm5/get_admhst.c b/src/lib/kadm5/get_admhst.c new file mode 100644 index 000000000..a398997d6 --- /dev/null +++ b/src/lib/kadm5/get_admhst.c @@ -0,0 +1,89 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute + * of Technology. + * + * For copying and distribution information, please see the file + * . + */ + +#ifndef lint +static char *rcsid = +"$Header$"; +#endif /* lint */ + +#include +#include +#include + +/* + * Given a Kerberos realm, find a host on which the Kerberos database + * administration server can be found. + * + * krb5_get_admhst takes a pointer to be filled in, a pointer to the name + * of the realm for which a server is desired, and an integer n, and + * returns (in h) the nth administrative host entry from the configuration + * file DEFAULT_CONFIG_FILENAME. + * + * If the realm is NULL, the default realm is used. + * + * On error, get_admhst returns 0. If all goes well, the routine + * returns 1. + * + * This is a temporary hack to allow us to find the nearest system running + * a Kerberos admin server. In the long run, this functionality will be + * provided by a nameserver. + */ +int +krb5_get_admhst(char *h, char *r, int n) +{ + FILE *cnffile; + char *realm = NULL; + char tr[BUFSIZ]; + char linebuf[BUFSIZ]; + char scratch[64]; + register int i; + int ret; + + if(r == NULL) { + if((ret = krb5_get_default_realm(&realm)) != 0) + return ret; + r = realm; + } + if ((cnffile = fopen(DEFAULT_CONFIG_FILENAME, "r")) == NULL) { + return(0); + } + if (fgets(linebuf, BUFSIZ, cnffile) == NULL) { + /* error reading */ + (void) fclose(cnffile); + return(0); + } + if (!strchr(linebuf, '\n')) { + /* didn't all fit into buffer, punt */ + (void) fclose(cnffile); + if(realm) + free(realm); + return(0); + } + for (i = 0; i < n; ) { + /* run through the file, looking for admin host */ + if (fgets(linebuf, BUFSIZ, cnffile) == NULL) { + (void) fclose(cnffile); + if(realm) + free(realm); + return(0); + } + /* need to scan for a token after 'admin' to make sure that + admin matched correctly */ + if (sscanf(linebuf, "%s %s admin %s", tr, h, scratch) != 3) + continue; + if (!strcmp(tr,r)) + i++; + } + (void) fclose(cnffile); + if(realm) + free(realm); + return(1); +} diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et new file mode 100644 index 000000000..446a38de1 --- /dev/null +++ b/src/lib/kadm5/kadm_err.et @@ -0,0 +1,54 @@ +# This is the KADM5 error table. The order of error codes +# defined in this file MUST match that specified in the API +# functionality specification, which is the master version. +# +error_table ovk +# vv 0 +error_code KADM5_FAILURE, "Operation failed for unspecified reason" +error_code KADM5_AUTH_GET, "Operation requires ``get'' privilege" +error_code KADM5_AUTH_ADD, "Operation requires ``add'' privilege" +error_code KADM5_AUTH_MODIFY, "Operation requires ``modify'' privilege" +error_code KADM5_AUTH_DELETE, "Operation requires ``delete'' privilege" +error_code KADM5_AUTH_INSUFFICIENT, "Insufficient authorization for operation" +error_code KADM5_BAD_DB, "Database inconsistency detected" +error_code KADM5_DUP, "Principal or policy already exists" +error_code KADM5_RPC_ERROR, "Communication failure with server" +error_code KADM5_NO_SRV, "No administration server found for realm" +error_code KADM5_BAD_HIST_KEY, "Password history principal key version mismatch" +error_code KADM5_NOT_INIT, "Connection to server not initialized" +error_code KADM5_UNK_PRINC, "Principal does not exist" +error_code KADM5_UNK_POLICY, "Policy does not exist" +error_code KADM5_BAD_MASK, "Invalid field mask for operation" +error_code KADM5_BAD_CLASS, "Invalid number of character classes" +error_code KADM5_BAD_LENGTH, "Invalid password length" +error_code KADM5_BAD_POLICY, "Illegal policy name" +error_code KADM5_BAD_PRINCIPAL, "Illegal principal name" +error_code KADM5_BAD_AUX_ATTR, "Invalid auxillary attributes" +error_code KADM5_BAD_HISTORY, "Invalid password history count" +error_code KADM5_BAD_MIN_PASS_LIFE, "Password minimum life is greater than password maximum life" +error_code KADM5_PASS_Q_TOOSHORT, "Password is too short" +error_code KADM5_PASS_Q_CLASS, "Password does not contain enough character classes" +error_code KADM5_PASS_Q_DICT, "Password is in the password dictionary" +error_code KADM5_PASS_REUSE, "Cannot reuse password" +error_code KADM5_PASS_TOOSOON, "Current password's minimum life has not expired" +error_code KADM5_POLICY_REF, "Policy is in use" +error_code KADM5_INIT, "Connection to server already initialized" +error_code KADM5_BAD_PASSWORD, "Incorrect password" +error_code KADM5_PROTECT_PRINCIPAL, "Cannot change protected principal" +error_code KADM5_BAD_SERVER_HANDLE, "Programmer error! Bad Admin server handle" +error_code KADM5_BAD_STRUCT_VERSION, "Programmer error! Bad API structure version" +error_code KADM5_OLD_STRUCT_VERSION, "API structure version specified by application is no longer supported (to fix, recompile application against current KADM5 API header files and libraries)" +error_code KADM5_NEW_STRUCT_VERSION, "API structure version specified by application is unknown to libraries (to fix, obtain current KADM5 API header files and libraries and recompile application)" +error_code KADM5_BAD_API_VERSION, "Programmer error! Bad API version" +error_code KADM5_OLD_LIB_API_VERSION, "API version specified by application is no longer supported by libraries (to fix, update application to adhere to current API version and recompile)" +error_code KADM5_OLD_SERVER_API_VERSION, "API version specified by application is no longer supported by server (to fix, update application to adhere to current API version and recompile)" +error_code KADM5_NEW_LIB_API_VERSION, "API version specified by application is unknown to libraries (to fix, obtain current KADM5 API header files and libraries and recompile application)" +error_code KADM5_NEW_SERVER_API_VERSION, "API version specified by application is unknown to server (to fix, obtain and install newest KADM5 Admin Server)" +error_code KADM5_SECURE_PRINC_MISSING, "Database error! Required KADM5 principal missing" +error_code KADM5_NO_RENAME_SALT, "The salt type of the specified principal does not support renaming" +error_code KADM5_BAD_CLIENT_PARAMS, "Illegal configuration parameter for remote KADM5 client" +error_code KADM5_BAD_SERVER_PARAMS, "Illegal configuration parameter for local KADM5 client" +error_code KADM5_AUTH_LIST, "Operation requires ``list'' privilege" +error_code KADM5_AUTH_CHANGEPW, "Operation requires ``change-password'' privilege" +error_code KADM5_GSS_ERROR, "GSS-API (or Kerberos) error" +end diff --git a/src/lib/kadm5/kadm_rpc.h b/src/lib/kadm5/kadm_rpc.h new file mode 100644 index 000000000..b0f97e6cb --- /dev/null +++ b/src/lib/kadm5/kadm_rpc.h @@ -0,0 +1,205 @@ +#include + +#include +#include + +struct cprinc_arg { + krb5_ui_4 api_version; + kadm5_principal_ent_rec rec; + long mask; + char *passwd; +}; +typedef struct cprinc_arg cprinc_arg; +bool_t xdr_cprinc_arg(); + +struct generic_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; +}; +typedef struct generic_ret generic_ret; +bool_t xdr_generic_ret(); + +struct dprinc_arg { + krb5_ui_4 api_version; + krb5_principal princ; +}; +typedef struct dprinc_arg dprinc_arg; +bool_t xdr_dprinc_arg(); + +struct mprinc_arg { + krb5_ui_4 api_version; + kadm5_principal_ent_rec rec; + long mask; +}; +typedef struct mprinc_arg mprinc_arg; +bool_t xdr_mprinc_arg(); + +struct rprinc_arg { + krb5_ui_4 api_version; + krb5_principal src; + krb5_principal dest; +}; +typedef struct rprinc_arg rprinc_arg; +bool_t xdr_rprinc_arg(); + +struct gprincs_arg { + krb5_ui_4 api_version; + char *exp; +}; +typedef struct gprincs_arg gprincs_arg; +bool_t xdr_gprincs_arg(); + +struct gprincs_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + char **princs; + int count; +}; +typedef struct gprincs_ret gprincs_ret; +bool_t xdr_gprincs_ret(); + +struct chpass_arg { + krb5_ui_4 api_version; + krb5_principal princ; + char *pass; +}; +typedef struct chpass_arg chpass_arg; +bool_t xdr_chpass_arg(); + +struct chrand_arg { + krb5_ui_4 api_version; + krb5_principal princ; +}; +typedef struct chrand_arg chrand_arg; +bool_t xdr_chrand_arg(); + +struct chrand_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + krb5_keyblock key; + krb5_keyblock *keys; + int n_keys; +}; +typedef struct chrand_ret chrand_ret; +bool_t xdr_chrand_ret(); + +struct gprinc_arg { + krb5_ui_4 api_version; + krb5_principal princ; + long mask; +}; +typedef struct gprinc_arg gprinc_arg; +bool_t xdr_gprinc_arg(); + +struct gprinc_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + kadm5_principal_ent_rec rec; +}; +typedef struct gprinc_ret gprinc_ret; +bool_t xdr_gprinc_ret(); +bool_t xdr_kadm5_ret_t(); +bool_t xdr_kadm5_principal_ent_rec(); +bool_t xdr_kadm5_policy_ent_rec(); +bool_t xdr_krb5_keyblock(); +bool_t xdr_krb5_principal(); +bool_t xdr_krb5_enctype(); +bool_t xdr_krb5_octet(); +bool_t xdr_krb5_int32(); +bool_t xdr_u_int32(); + +struct cpol_arg { + krb5_ui_4 api_version; + kadm5_policy_ent_rec rec; + long mask; +}; +typedef struct cpol_arg cpol_arg; +bool_t xdr_cpol_arg(); + +struct dpol_arg { + krb5_ui_4 api_version; + char *name; +}; +typedef struct dpol_arg dpol_arg; +bool_t xdr_dpol_arg(); + +struct mpol_arg { + krb5_ui_4 api_version; + kadm5_policy_ent_rec rec; + long mask; +}; +typedef struct mpol_arg mpol_arg; +bool_t xdr_mpol_arg(); + +struct gpol_arg { + krb5_ui_4 api_version; + char *name; +}; +typedef struct gpol_arg gpol_arg; +bool_t xdr_gpol_arg(); + +struct gpol_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + kadm5_policy_ent_rec rec; +}; +typedef struct gpol_ret gpol_ret; +bool_t xdr_gpol_ret(); + +struct gpols_arg { + krb5_ui_4 api_version; + char *exp; +}; +typedef struct gpols_arg gpols_arg; +bool_t xdr_gpols_arg(); + +struct gpols_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + char **pols; + int count; +}; +typedef struct gpols_ret gpols_ret; +bool_t xdr_gpols_ret(); + +struct getprivs_ret { + krb5_ui_4 api_version; + kadm5_ret_t code; + long privs; +}; +typedef struct getprivs_ret getprivs_ret; +bool_t xdr_getprivs_ret(); + +#define KADM ((krb5_ui_4)2112) +#define KADMVERS ((krb5_ui_4)2) +#define CREATE_PRINCIPAL ((krb5_ui_4)1) +extern generic_ret *create_principal_1(); +#define DELETE_PRINCIPAL ((krb5_ui_4)2) +extern generic_ret *delete_principal_1(); +#define MODIFY_PRINCIPAL ((krb5_ui_4)3) +extern generic_ret *modify_principal_1(); +#define RENAME_PRINCIPAL ((krb5_ui_4)4) +extern generic_ret *rename_principal_1(); +#define GET_PRINCIPAL ((krb5_ui_4)5) +extern gprinc_ret *get_principal_1(); +#define CHPASS_PRINCIPAL ((krb5_ui_4)6) +extern generic_ret *chpass_principal_1(); +#define CHRAND_PRINCIPAL ((krb5_ui_4)7) +extern chrand_ret *chrand_principal_1(); +#define CREATE_POLICY ((krb5_ui_4)8) +extern generic_ret *create_policy_1(); +#define DELETE_POLICY ((krb5_ui_4)9) +extern generic_ret *delete_policy_1(); +#define MODIFY_POLICY ((krb5_ui_4)10) +extern generic_ret *modify_policy_1(); +#define GET_POLICY ((krb5_ui_4)11) +extern gpol_ret *get_policy_1(); +#define GET_PRIVS ((krb5_ui_4)12) +extern getprivs_ret *get_privs_1(); +#define INIT ((krb5_ui_4)13) +extern generic_ret *init_1(); +#define GET_PRINCS ((krb5_ui_4) 14) +extern gprincs_ret *get_princs_1(); +#define GET_POLS ((krb5_ui_4) 15) +extern gpols_ret *get_pols_1(); + diff --git a/src/lib/kadm5/kadm_rpc_xdr.c b/src/lib/kadm5/kadm_rpc_xdr.c new file mode 100644 index 000000000..6cce9391e --- /dev/null +++ b/src/lib/kadm5/kadm_rpc_xdr.c @@ -0,0 +1,830 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include + +static bool_t +_xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp, + int v); + +/* + * Function: xdr_ui_4 + * + * Purpose: XDR function which serves as a wrapper for xdr_u_int32, + * to prevent compiler warnings about type clashes between u_int32 + * and krb5_ui_4. + */ +bool_t xdr_ui_4(XDR *xdrs, krb5_ui_4 *objp) +{ + /* Assumes that krb5_ui_4 and u_int32 are both four bytes long. + This should not be a harmful assumption. */ + return xdr_u_int32(xdrs, (rpc_u_int32 *) objp); +} + + +/* + * Function: xdr_nullstring + * + * Purpose: XDR function for "strings" that are either NULL-terminated + * or NULL. + */ +bool_t xdr_nullstring(XDR *xdrs, char **objp) +{ + u_int size; + + if (xdrs->x_op == XDR_ENCODE) { + if (*objp == NULL) + size = 0; + else + size = strlen(*objp) + 1; + } + if (! xdr_u_int(xdrs, &size)) { + return FALSE; + } + switch (xdrs->x_op) { + case XDR_DECODE: + if (size == 0) { + *objp = NULL; + return TRUE; + } else if (*objp == NULL) { + *objp = (char *) mem_alloc(size); + if (*objp == NULL) { + errno = ENOMEM; + return FALSE; + } + } + return (xdr_opaque(xdrs, *objp, size)); + + case XDR_ENCODE: + if (size != 0) + return (xdr_opaque(xdrs, *objp, size)); + return TRUE; + + case XDR_FREE: + if (*objp != NULL) + mem_free(*objp, size); + *objp = NULL; + return TRUE; + } + + return FALSE; +} + +/* + * Function: xdr_nulltype + * + * Purpose: XDR function for arbitrary pointer types that are either + * NULL or contain data. + */ +bool_t xdr_nulltype(XDR *xdrs, void **objp, xdrproc_t proc) +{ + bool_t null; + + switch (xdrs->x_op) { + case XDR_DECODE: + if (!xdr_bool(xdrs, &null)) + return FALSE; + if (null) { + *objp = NULL; + return TRUE; + } + return (*proc)(xdrs, objp); + + case XDR_ENCODE: + if (*objp == NULL) + null = TRUE; + else + null = FALSE; + if (!xdr_bool(xdrs, &null)) + return FALSE; + if (null == FALSE) + return (*proc)(xdrs, objp); + return TRUE; + + case XDR_FREE: + if (*objp) + return (*proc)(xdrs, objp); + return TRUE; + } + + return FALSE; +} + +bool_t +xdr_krb5_timestamp(XDR *xdrs, krb5_timestamp *objp) +{ + /* This assumes that int32 and krb5_timestamp are the same size. + This shouldn't be a problem, since we've got a unit test which + checks for this. */ + if (!xdr_int32(xdrs, (rpc_int32 *) objp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_krb5_kvno(XDR *xdrs, krb5_kvno *objp) +{ + unsigned char tmp; + + if (xdrs->x_op == XDR_ENCODE) + tmp = (unsigned char) *objp; + + if (!xdr_u_char(xdrs, &tmp)) + return (FALSE); + + if (xdrs->x_op == XDR_DECODE) + *objp = (krb5_kvno) tmp; + + return (TRUE); +} + +bool_t +xdr_krb5_deltat(XDR *xdrs, krb5_deltat *objp) +{ + /* This assumes that int32 and krb5_deltat are the same size. + This shouldn't be a problem, since we've got a unit test which + checks for this. */ + if (!xdr_int32(xdrs, (rpc_int32 *) objp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_krb5_flags(XDR *xdrs, krb5_flags *objp) +{ + /* This assumes that int32 and krb5_flags are the same size. + This shouldn't be a problem, since we've got a unit test which + checks for this. */ + if (!xdr_int32(xdrs, (rpc_int32 *) objp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_krb5_ui_4(XDR *xdrs, krb5_ui_4 *objp) +{ + if (!xdr_u_int32(xdrs, (rpc_u_int32 *) objp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_krb5_int16(XDR *xdrs, krb5_int16 *objp) +{ + int tmp; + + tmp = (int) *objp; + + if (!xdr_int(xdrs, &tmp)) + return(FALSE); + + *objp = (krb5_int16) tmp; + + return(TRUE); +} + +bool_t xdr_krb5_key_data_nocontents(XDR *xdrs, krb5_key_data *objp) +{ + /* + * Note that this function intentionally DOES NOT tranfer key + * length or contents! xdr_krb5_key_data in adb_xdr.c does. + */ + + if (xdrs->x_op == XDR_DECODE) + memset((char *) objp, 0, sizeof(krb5_key_data)); + + if (!xdr_krb5_int16(xdrs, &objp->key_data_ver)) { + return (FALSE); + } + if (!xdr_krb5_int16(xdrs, &objp->key_data_kvno)) { + return (FALSE); + } + if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) { + return (FALSE); + } + if (objp->key_data_ver > 1) { + if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0])) { + return (FALSE); + } + } + return (TRUE); +} + +bool_t xdr_krb5_tl_data(XDR *xdrs, krb5_tl_data **tl_data_head) +{ + krb5_tl_data *tl, *tl2; + bool_t more; + int len; + + switch (xdrs->x_op) { + case XDR_FREE: + case XDR_ENCODE: + tl = *tl_data_head; + while (1) { + more = (tl != NULL); + if (!xdr_bool(xdrs, &more)) + return FALSE; + if (tl == NULL) + break; + if (!xdr_krb5_int16(xdrs, &tl->tl_data_type)) + return FALSE; + len = tl->tl_data_length; + if (!xdr_bytes(xdrs, (char **) &tl->tl_data_contents, &len, ~0)) + return FALSE; + tl = tl->tl_data_next; + } + break; + + case XDR_DECODE: + tl = NULL; + while (1) { + if (!xdr_bool(xdrs, &more)) + return FALSE; + if (more == FALSE) + break; + tl2 = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); + if (tl2 == NULL) + return FALSE; + memset((char *) tl2, 0, sizeof(krb5_tl_data)); + if (!xdr_krb5_int16(xdrs, &tl2->tl_data_type)) + return FALSE; + if (!xdr_bytes(xdrs, (char **)&tl2->tl_data_contents, &len, ~0)) + return FALSE; + tl2->tl_data_length = len; + + tl2->tl_data_next = tl; + tl = tl2; + } + + *tl_data_head = tl; + break; + } + + return TRUE; +} + +bool_t +xdr_kadm5_ret_t(XDR *xdrs, kadm5_ret_t *objp) +{ + rpc_u_int32 tmp; + + if (xdrs->x_op == XDR_ENCODE) + tmp = (rpc_u_int32) *objp; + + if (!xdr_u_int32(xdrs, &tmp)) + return (FALSE); + + if (xdrs->x_op == XDR_DECODE) + *objp = (kadm5_ret_t) tmp; + + return (TRUE); +} + +bool_t xdr_kadm5_principal_ent_rec_v1(XDR *xdrs, + kadm5_principal_ent_rec *objp) +{ + return _xdr_kadm5_principal_ent_rec(xdrs, objp, KADM5_API_VERSION_1); +} + +bool_t xdr_kadm5_principal_ent_rec(XDR *xdrs, + kadm5_principal_ent_rec *objp) +{ + return _xdr_kadm5_principal_ent_rec(xdrs, objp, KADM5_API_VERSION_2); +} + +static bool_t +_xdr_kadm5_principal_ent_rec(XDR *xdrs, kadm5_principal_ent_rec *objp, + int v) +{ + unsigned int n; + + if (!xdr_krb5_principal(xdrs, &objp->principal)) { + return (FALSE); + } + if (!xdr_krb5_timestamp(xdrs, &objp->princ_expire_time)) { + return (FALSE); + } + if (!xdr_krb5_timestamp(xdrs, &objp->last_pwd_change)) { + return (FALSE); + } + if (!xdr_krb5_timestamp(xdrs, &objp->pw_expiration)) { + return (FALSE); + } + if (!xdr_krb5_deltat(xdrs, &objp->max_life)) { + return (FALSE); + } + if (v == KADM5_API_VERSION_1) { + if (!xdr_krb5_principal(xdrs, &objp->mod_name)) { + return (FALSE); + } + } else { + if (!xdr_nulltype(xdrs, (void **) &objp->mod_name, + xdr_krb5_principal)) { + return (FALSE); + } + } + if (!xdr_krb5_timestamp(xdrs, &objp->mod_date)) { + return (FALSE); + } + if (!xdr_krb5_flags(xdrs, &objp->attributes)) { + return (FALSE); + } + if (!xdr_krb5_kvno(xdrs, &objp->kvno)) { + return (FALSE); + } + if (!xdr_krb5_kvno(xdrs, &objp->mkvno)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->policy)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->aux_attributes)) { + return (FALSE); + } + if (v != KADM5_API_VERSION_1) { + if (!xdr_krb5_deltat(xdrs, &objp->max_renewable_life)) { + return (FALSE); + } + if (!xdr_krb5_timestamp(xdrs, &objp->last_success)) { + return (FALSE); + } + if (!xdr_krb5_timestamp(xdrs, &objp->last_failed)) { + return (FALSE); + } + if (!xdr_krb5_kvno(xdrs, &objp->fail_auth_count)) { + return (FALSE); + } + if (!xdr_krb5_int16(xdrs, &objp->n_key_data)) { + return (FALSE); + } + if (!xdr_krb5_int16(xdrs, &objp->n_tl_data)) { + return (FALSE); + } + if (!xdr_nulltype(xdrs, (void **) &objp->tl_data, + xdr_krb5_tl_data)) { + return FALSE; + } + n = objp->n_key_data; + if (!xdr_array(xdrs, (caddr_t *) &objp->key_data, + &n, ~0, sizeof(krb5_key_data), + xdr_krb5_key_data_nocontents)) { + return (FALSE); + } + } + return (TRUE); +} + +bool_t +xdr_kadm5_policy_ent_rec(XDR *xdrs, kadm5_policy_ent_rec *objp) +{ + if (!xdr_nullstring(xdrs, &objp->policy)) { + return (FALSE); + } + /* these all used to be u_int32, but it's stupid for sized types + to be exposed at the api, and they're the same as longs on the + wire. */ + if (!xdr_long(xdrs, &objp->pw_min_life)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->pw_max_life)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->pw_min_length)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->pw_min_classes)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->pw_history_num)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->policy_refcnt)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_cprinc_arg(XDR *xdrs, cprinc_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (objp->api_version == KADM5_API_VERSION_1) { + if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { + return (FALSE); + } + } else { + if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { + return (FALSE); + } + } + if (!xdr_long(xdrs, &objp->mask)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->passwd)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_generic_ret(XDR *xdrs, generic_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + return(TRUE); +} + +bool_t +xdr_dprinc_arg(XDR *xdrs, dprinc_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->princ)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mprinc_arg(XDR *xdrs, mprinc_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (objp->api_version == KADM5_API_VERSION_1) { + if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { + return (FALSE); + } + } else { + if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { + return (FALSE); + } + } + if (!xdr_long(xdrs, &objp->mask)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_rprinc_arg(XDR *xdrs, rprinc_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->src)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->dest)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gprincs_arg(XDR *xdrs, gprincs_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->exp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gprincs_ret(XDR *xdrs, gprincs_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + if (objp->code == KADM5_OK) { + if (!xdr_int(xdrs, &objp->count)) { + return (FALSE); + } + if (!xdr_array(xdrs, (caddr_t *) &objp->princs, + (unsigned int *) &objp->count, ~0, + sizeof(char *), xdr_nullstring)) { + return (FALSE); + } + } + return (TRUE); +} + +bool_t +xdr_chpass_arg(XDR *xdrs, chpass_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->princ)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->pass)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_chrand_arg(XDR *xdrs, chrand_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->princ)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_chrand_ret(XDR *xdrs, chrand_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + if (objp->api_version == KADM5_API_VERSION_1) { + if(objp->code == KADM5_OK) { + if (!xdr_krb5_keyblock(xdrs, &objp->key)) { + return (FALSE); + } + } + } else { + if (objp->code == KADM5_OK) { + if (!xdr_array(xdrs, (char **)&objp->keys, &objp->n_keys, ~0, + sizeof(krb5_keyblock), + xdr_krb5_keyblock)) + return FALSE; + } + } + + return (TRUE); +} + +bool_t +xdr_gprinc_arg(XDR *xdrs, gprinc_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_krb5_principal(xdrs, &objp->princ)) { + return (FALSE); + } + if ((objp->api_version > KADM5_API_VERSION_1) && + !xdr_long(xdrs, &objp->mask)) { + return FALSE; + } + + return (TRUE); +} + +bool_t +xdr_gprinc_ret(XDR *xdrs, gprinc_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + if(objp->code == KADM5_OK) { + if (objp->api_version == KADM5_API_VERSION_1) { + if (!xdr_kadm5_principal_ent_rec_v1(xdrs, &objp->rec)) { + return (FALSE); + } + } else { + if (!xdr_kadm5_principal_ent_rec(xdrs, &objp->rec)) { + return (FALSE); + } + } + } + return (TRUE); +} + +bool_t +xdr_cpol_arg(XDR *xdrs, cpol_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->mask)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_dpol_arg(XDR *xdrs, dpol_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->name)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_mpol_arg(XDR *xdrs, mpol_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) { + return (FALSE); + } + if (!xdr_long(xdrs, &objp->mask)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gpol_arg(XDR *xdrs, gpol_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->name)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gpol_ret(XDR *xdrs, gpol_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + if(objp->code == KADM5_OK) { + if (!xdr_kadm5_policy_ent_rec(xdrs, &objp->rec)) + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gpols_arg(XDR *xdrs, gpols_arg *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_nullstring(xdrs, &objp->exp)) { + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gpols_ret(XDR *xdrs, gpols_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (!xdr_kadm5_ret_t(xdrs, &objp->code)) { + return (FALSE); + } + if (objp->code == KADM5_OK) { + if (!xdr_int(xdrs, &objp->count)) { + return (FALSE); + } + if (!xdr_array(xdrs, (caddr_t *) &objp->pols, + (unsigned int *) &objp->count, ~0, + sizeof(char *), xdr_nullstring)) { + return (FALSE); + } + } + return (TRUE); +} + +bool_t xdr_getprivs_ret(XDR *xdrs, getprivs_ret *objp) +{ + if (!xdr_ui_4(xdrs, &objp->api_version)) { + return (FALSE); + } + if (! xdr_kadm5_ret_t(xdrs, &objp->code) || + ! xdr_long(xdrs, &objp->privs)) + return FALSE; + return TRUE; +} + +bool_t +xdr_krb5_principal(XDR *xdrs, krb5_principal *objp) +{ + int ret; + char *p = NULL; + krb5_principal pr = NULL; + static krb5_context context = NULL; + + /* using a static context here is ugly, but should work + ok, and the other solutions are even uglier */ + + if (!context && + krb5_init_context(&context)) + return(FALSE); + + switch(xdrs->x_op) { + case XDR_ENCODE: + if((ret = krb5_unparse_name(context, *objp, &p)) != 0) + return FALSE; + if(!xdr_nullstring(xdrs, &p)) + return FALSE; + free(p); + break; + case XDR_DECODE: + if(!xdr_nullstring(xdrs, &p)) + return FALSE; + ret = krb5_parse_name(context, p, &pr); + if(ret != 0) + return FALSE; + *objp = pr; + free(p); + break; + case XDR_FREE: + if(*objp != NULL) + krb5_free_principal(context, *objp); + break; + } + return TRUE; +} + +bool_t +xdr_krb5_octet(XDR *xdrs, krb5_octet *objp) +{ + if (!xdr_u_char(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_krb5_enctype(XDR *xdrs, krb5_enctype *objp) +{ + /* + * This used to be xdr_krb5_keytype, but keytypes and enctypes have + * been merged into only enctypes. However, randkey_principal + * already ensures that only a key of ENCTYPE_DES_CBC_CRC will be + * returned to v1 clients, and ENCTYPE_DES_CBC_CRC has the same + * value as KEYTYPE_DES used too, which is what all v1 clients + * expect. Therefore, IMHO, just encoding whatever enctype we get + * is safe. + */ + + if (!xdr_u_int(xdrs, (unsigned int *) objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_krb5_keyblock(XDR *xdrs, krb5_keyblock *objp) +{ + /* XXX This only works because free_keyblock assumes ->contents + is allocated by malloc() */ + + if(!xdr_krb5_enctype(xdrs, &objp->enctype)) + return FALSE; + if(!xdr_bytes(xdrs, (char **) &objp->contents, (unsigned int *) + &objp->length, ~0)) + return FALSE; + return TRUE; +} + diff --git a/src/lib/kadm5/logger.c b/src/lib/kadm5/logger.c new file mode 100644 index 000000000..924fb9a23 --- /dev/null +++ b/src/lib/kadm5/logger.c @@ -0,0 +1,940 @@ +/* + * lib/kadm/logger.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. + * + */ + +#if !defined(_MSDOS) + +/* + * logger.c - Handle logging functions for those who want it. + */ +#include "k5-int.h" +#include "adm_proto.h" +#include "com_err.h" +#include +#ifdef HAVE_SYSLOG_H +#include +#endif /* HAVE_SYSLOG_H */ +#ifdef HAVE_STDARG_H +#include +#else /* HAVE_STDARG_H */ +#include +#endif /* HAVE_STDARG_H */ + +#define KRB5_KLOG_MAX_ERRMSG_SIZE 1024 +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif /* MAXHOSTNAMELEN */ + +/* This is to assure that we have at least one match in the syslog stuff */ +#ifndef LOG_AUTH +#define LOG_AUTH 0 +#endif /* LOG_AUTH */ +#ifndef LOG_ERR +#define LOG_ERR 0 +#endif /* LOG_ERR */ + +static const char lspec_parse_err_1[] = "%s: cannot parse <%s>\n"; +static const char lspec_parse_err_2[] = "%s: warning - logging entry syntax error\n"; +static const char log_file_err[] = "%s: error writing to %s\n"; +static const char log_device_err[] = "%s: error writing to %s device\n"; +static const char log_ufo_string[] = "???"; +static const char log_emerg_string[] = "EMERGENCY"; +static const char log_alert_string[] = "ALERT"; +static const char log_crit_string[] = "CRITICAL"; +static const char log_err_string[] = "Error"; +static const char log_warning_string[] = "Warning"; +static const char log_notice_string[] = "Notice"; +static const char log_info_string[] = "info"; +static const char log_debug_string[] = "debug"; + +/* + * Output logging. + * + * Output logging is now controlled by the configuration file. We can specify + * the following syntaxes under the [logging]->entity specification. + * FILE + * SYSLOG[=[:]] + * STDERR + * CONSOLE + * DEVICE= + * + * Where: + * is ":" for open/append, "=" for open/create. + * is a valid path name. + * is one of: (default = ERR) + * EMERG + * ALERT + * CRIT + * ERR + * WARNING + * NOTICE + * INFO + * DEBUG + * is one of: (default = AUTH) + * KERN + * USER + * MAIL + * DAEMON + * AUTH + * LPR + * NEWS + * UUCP + * CRON + * LOCAL0..LOCAL7 + * is a valid device specification. + */ +struct log_entry { + enum log_type { K_LOG_FILE, + K_LOG_SYSLOG, + K_LOG_STDERR, + K_LOG_CONSOLE, + K_LOG_DEVICE, + K_LOG_NONE } log_type; + krb5_pointer log_2free; + union log_union { + struct log_file { + FILE *lf_filep; + char *lf_fname; + } log_file; + struct log_syslog { + int ls_facility; + int ls_severity; + } log_syslog; + struct log_device { + FILE *ld_filep; + char *ld_devname; + } log_device; + } log_union; +}; +#define lfu_filep log_union.log_file.lf_filep +#define lfu_fname log_union.log_file.lf_fname +#define lsu_facility log_union.log_syslog.ls_facility +#define lsu_severity log_union.log_syslog.ls_severity +#define ldu_filep log_union.log_device.ld_filep +#define ldu_devname log_union.log_device.ld_devname + +struct log_control { + struct log_entry *log_entries; + int log_nentries; + char *log_whoami; + char *log_hostname; + krb5_boolean log_opened; +}; + +static struct log_control log_control = { + (struct log_entry *) NULL, + 0, + (char *) NULL, + (char *) NULL, + 0 +}; +static struct log_entry def_log_entry; + +/* + * These macros define any special processing that needs to happen for + * devices. For unix, of course, this is hardly anything. + */ +#define DEVICE_OPEN(d, m) fopen(d, m) +#define CONSOLE_OPEN(m) fopen("/dev/console", m) +#define DEVICE_PRINT(f, m) ((fprintf(f, m) >= 0) ? \ + (fprintf(f, "\r\n"), fflush(f), 0) : \ + -1) +#define DEVICE_CLOSE(d) fclose(d) + + +/* + * klog_com_err_proc() - Handle com_err(3) messages as specified by the + * profile. + */ +static void +klog_com_err_proc(whoami, code, format, ap) + const char *whoami; + long code; + const char *format; + va_list ap; +{ + char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; + int lindex; + char *actual_format; +#ifdef HAVE_SYSLOG + int log_pri = -1; +#endif /* HAVE_SYSLOG */ + char *cp; + char *syslogp; + + /* Make the header */ + sprintf(outbuf, "%s: ", whoami); + /* + * Squirrel away address after header for syslog since syslog makes + * a header + */ + syslogp = &outbuf[strlen(outbuf)]; + + /* If reporting an error message, separate it. */ + if (code) { + strcat(outbuf, error_message(code)); + strcat(outbuf, " - "); + } + cp = &outbuf[strlen(outbuf)]; + + actual_format = (char *) format; +#ifdef HAVE_SYSLOG + /* + * This is an unpleasant hack. If the first character is less than + * 8, then we assume that it is a priority. + * + * Since it is not guaranteed that there is a direct mapping between + * syslog priorities (e.g. Ultrix and old BSD), we resort to this + * intermediate representation. + */ + if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) { + actual_format = (char *) (format + 1); + switch ((unsigned char) *format) { +#ifdef LOG_EMERG + case 1: + log_pri = LOG_EMERG; + break; +#endif /* LOG_EMERG */ +#ifdef LOG_ALERT + case 2: + log_pri = LOG_ALERT; + break; +#endif /* LOG_ALERT */ +#ifdef LOG_CRIT + case 3: + log_pri = LOG_CRIT; + break; +#endif /* LOG_CRIT */ + default: + case 4: + log_pri = LOG_ERR; + break; +#ifdef LOG_WARNING + case 5: + log_pri = LOG_WARNING; + break; +#endif /* LOG_WARNING */ +#ifdef LOG_NOTICE + case 6: + log_pri = LOG_NOTICE; + break; +#endif /* LOG_NOTICE */ +#ifdef LOG_INFO + case 7: + log_pri = LOG_INFO; + break; +#endif /* LOG_INFO */ +#ifdef LOG_DEBUG + case 8: + log_pri = LOG_DEBUG; + break; +#endif /* LOG_DEBUG */ + } + } +#endif /* HAVE_SYSLOG */ + + /* Now format the actual message */ +#if HAVE_VSPRINTF + vsprintf(cp, actual_format, ap); +#else /* HAVE_VSPRINTF */ + sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1], + ((int *) ap)[2], ((int *) ap)[3], + ((int *) ap)[4], ((int *) ap)[5]); +#endif /* HAVE_VSPRINTF */ + + /* + * Now that we have the message formatted, perform the output to each + * logging specification. + */ + for (lindex = 0; lindex < log_control.log_nentries; lindex++) { + switch (log_control.log_entries[lindex].log_type) { + case K_LOG_FILE: + case K_LOG_STDERR: + /* + * Files/standard error. + */ + if (fprintf(log_control.log_entries[lindex].lfu_filep, + outbuf) < 0) { + /* Attempt to report error */ + fprintf(stderr, log_file_err, whoami, + log_control.log_entries[lindex].lfu_fname); + } + else { + fprintf(log_control.log_entries[lindex].lfu_filep, "\n"); + fflush(log_control.log_entries[lindex].lfu_filep); + } + break; + case K_LOG_CONSOLE: + case K_LOG_DEVICE: + /* + * Devices (may need special handling) + */ + if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, + outbuf) < 0) { + /* Attempt to report error */ + fprintf(stderr, log_device_err, whoami, + log_control.log_entries[lindex].ldu_devname); + } + break; +#ifdef HAVE_SYSLOG + case K_LOG_SYSLOG: + /* + * System log. + */ + /* + * If we have specified a priority through our hackery, then + * use it, otherwise use the default. + */ + if (log_pri >= 0) + log_pri |= log_control.log_entries[lindex].lsu_facility; + else + log_pri = log_control.log_entries[lindex].lsu_facility | + log_control.log_entries[lindex].lsu_severity; + + /* Log the message with our header trimmed off */ + syslog(log_pri, syslogp); + break; +#endif /* HAVE_SYSLOG */ + default: + break; + } + } +} + +/* + * krb5_klog_init() - Initialize logging. + * + * This routine parses the syntax described above to specify destinations for + * com_err(3) or krb5_klog_syslog() messages generated by the caller. + * + * Parameters: + * kcontext - Kerberos context. + * ename - Entity name as it is to appear in the profile. + * whoami - Entity name as it is to appear in error output. + * do_com_err - Take over com_err(3) processing. + * + * Implicit inputs: + * stderr - This is where STDERR output goes. + * + * Implicit outputs: + * log_nentries - Number of log entries, both valid and invalid. + * log_control - List of entries (log_nentries long) which contains + * data for klog_com_err_proc() to use to determine + * where/how to send output. + */ +krb5_error_code +krb5_klog_init(kcontext, ename, whoami, do_com_err) + krb5_context kcontext; + char *ename; + char *whoami; + krb5_boolean do_com_err; +{ + const char *logging_profent[3]; + const char *logging_defent[3]; + char **logging_specs; + int i, ngood; + char *cp, *cp2; + char savec; + int error; + int do_openlog, log_facility; + FILE *f; + + /* Initialize */ + do_openlog = 0; + log_facility = 0; + + /* + * Look up [logging]-> in the profile. If that doesn't + * succeed, then look for [logging]->default. + */ + logging_profent[0] = "logging"; + logging_profent[1] = ename; + logging_profent[2] = (char *) NULL; + logging_defent[0] = "logging"; + logging_defent[1] = "default"; + logging_defent[2] = (char *) NULL; + logging_specs = (char **) NULL; + ngood = 0; + log_control.log_nentries = 0; + if (!profile_get_values(kcontext->profile, + logging_profent, + &logging_specs) || + !profile_get_values(kcontext->profile, + logging_defent, + &logging_specs)) { + /* + * We have a match, so we first count the number of elements + */ + for (log_control.log_nentries = 0; + logging_specs[log_control.log_nentries]; + log_control.log_nentries++); + + /* + * Now allocate our structure. + */ + log_control.log_entries = (struct log_entry *) + malloc(log_control.log_nentries * sizeof(struct log_entry)); + if (log_control.log_entries) { + /* + * Scan through the list. + */ + for (i=0; i + * so, trim off the leading and trailing whitespace here. + */ + for (cp = logging_specs[i]; isspace(*cp); cp++); + for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1]; + isspace(*cp2); cp2--); + cp2++; + *cp2 = '\0'; + /* + * Is this a file? + */ + if (!strncasecmp(cp, "FILE", 4)) { + /* + * Check for append/overwrite, then open the file. + */ + if (cp[4] == ':' || cp[4] == '=') { + f = fopen(&cp[5], (cp[4] == ':') ? "a+" : "w"); + if (f) { + log_control.log_entries[i].lfu_filep = f; + log_control.log_entries[i].log_type = K_LOG_FILE; + log_control.log_entries[i].lfu_fname = &cp[5]; + } else { + fprintf(stderr,"Couldn't open log file %s: %s\n", + &cp[5], error_message(errno)); + continue; + } + } + } +#ifdef HAVE_SYSLOG + /* + * Is this a syslog? + */ + else if (!strncasecmp(cp, "SYSLOG", 6)) { + error = 0; + log_control.log_entries[i].lsu_facility = LOG_AUTH; + log_control.log_entries[i].lsu_severity = LOG_ERR; + /* + * Is there a severify specified? + */ + if (cp[6] == ':') { + /* + * Find the end of the severity. + */ + if (cp2 = strchr(&cp[7], ':')) { + savec = *cp2; + *cp2 = '\0'; + cp2++; + } + + /* + * Match a severity. + */ + if (!strcasecmp(&cp[7], "ERR")) { + log_control.log_entries[i].lsu_severity = LOG_ERR; + } +#ifdef LOG_EMERG + else if (!strcasecmp(&cp[7], "EMERG")) { + log_control.log_entries[i].lsu_severity = + LOG_EMERG; + } +#endif /* LOG_EMERG */ +#ifdef LOG_ALERT + else if (!strcasecmp(&cp[7], "ALERT")) { + log_control.log_entries[i].lsu_severity = + LOG_ALERT; + } +#endif /* LOG_ALERT */ +#ifdef LOG_CRIT + else if (!strcasecmp(&cp[7], "CRIT")) { + log_control.log_entries[i].lsu_severity = LOG_CRIT; + } +#endif /* LOG_CRIT */ +#ifdef LOG_WARNING + else if (!strcasecmp(&cp[7], "WARNING")) { + log_control.log_entries[i].lsu_severity = + LOG_WARNING; + } +#endif /* LOG_WARNING */ +#ifdef LOG_NOTICE + else if (!strcasecmp(&cp[7], "NOTICE")) { + log_control.log_entries[i].lsu_severity = + LOG_NOTICE; + } +#endif /* LOG_NOTICE */ +#ifdef LOG_INFO + else if (!strcasecmp(&cp[7], "INFO")) { + log_control.log_entries[i].lsu_severity = LOG_INFO; + } +#endif /* LOG_INFO */ +#ifdef LOG_DEBUG + else if (!strcasecmp(&cp[7], "DEBUG")) { + log_control.log_entries[i].lsu_severity = + LOG_DEBUG; + } +#endif /* LOG_DEBUG */ + else + error = 1; + + /* + * If there is a facility present, then parse that. + */ + if (cp2) { + if (!strcasecmp(cp2, "AUTH")) { + log_control.log_entries[i].lsu_facility = LOG_AUTH; + } +#ifdef LOG_KERN + else if (!strcasecmp(cp2, "KERN")) { + log_control.log_entries[i].lsu_facility = LOG_KERN; + } +#endif /* LOG_KERN */ +#ifdef LOG_USER + else if (!strcasecmp(cp2, "USER")) { + log_control.log_entries[i].lsu_facility = LOG_USER; + } +#endif /* LOG_USER */ +#ifdef LOG_MAIL + else if (!strcasecmp(cp2, "MAIL")) { + log_control.log_entries[i].lsu_facility = LOG_MAIL; + } +#endif /* LOG_MAIL */ +#ifdef LOG_DAEMON + else if (!strcasecmp(cp2, "DAEMON")) { + log_control.log_entries[i].lsu_facility = LOG_DAEMON; + } +#endif /* LOG_DAEMON */ +#ifdef LOG_LPR + else if (!strcasecmp(cp2, "LPR")) { + log_control.log_entries[i].lsu_facility = LOG_LPR; + } +#endif /* LOG_LPR */ +#ifdef LOG_NEWS + else if (!strcasecmp(cp2, "NEWS")) { + log_control.log_entries[i].lsu_facility = LOG_NEWS; + } +#endif /* LOG_NEWS */ +#ifdef LOG_UUCP + else if (!strcasecmp(cp2, "UUCP")) { + log_control.log_entries[i].lsu_facility = LOG_UUCP; + } +#endif /* LOG_UUCP */ +#ifdef LOG_CRON + else if (!strcasecmp(cp2, "CRON")) { + log_control.log_entries[i].lsu_facility = LOG_CRON; + } +#endif /* LOG_CRON */ +#ifdef LOG_LOCAL0 + else if (!strcasecmp(cp2, "LOCAL0")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL0; + } +#endif /* LOG_LOCAL0 */ +#ifdef LOG_LOCAL1 + else if (!strcasecmp(cp2, "LOCAL1")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL1; + } +#endif /* LOG_LOCAL1 */ +#ifdef LOG_LOCAL2 + else if (!strcasecmp(cp2, "LOCAL2")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL2; + } +#endif /* LOG_LOCAL2 */ +#ifdef LOG_LOCAL3 + else if (!strcasecmp(cp2, "LOCAL3")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL3; + } +#endif /* LOG_LOCAL3 */ +#ifdef LOG_LOCAL4 + else if (!strcasecmp(cp2, "LOCAL4")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL4; + } +#endif /* LOG_LOCAL4 */ +#ifdef LOG_LOCAL5 + else if (!strcasecmp(cp2, "LOCAL5")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL5; + } +#endif /* LOG_LOCAL5 */ +#ifdef LOG_LOCAL6 + else if (!strcasecmp(cp2, "LOCAL6")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL6; + } +#endif /* LOG_LOCAL6 */ +#ifdef LOG_LOCAL7 + else if (!strcasecmp(cp2, "LOCAL7")) { + log_control.log_entries[i].lsu_facility = LOG_LOCAL7; + } +#endif /* LOG_LOCAL7 */ + cp2--; + *cp2 = savec; + } + } + if (!error) { + log_control.log_entries[i].log_type = K_LOG_SYSLOG; + do_openlog = 1; + log_facility = log_control.log_entries[i].lsu_facility; + } + } +#endif /* HAVE_SYSLOG */ + /* + * Is this a standard error specification? + */ + else if (!strcasecmp(cp, "STDERR")) { + if (log_control.log_entries[i].lfu_filep = + fdopen(fileno(stderr), "a+")) { + log_control.log_entries[i].log_type = K_LOG_STDERR; + log_control.log_entries[i].lfu_fname = + "standard error"; + } + } + /* + * Is this a specification of the console? + */ + else if (!strcasecmp(cp, "CONSOLE")) { + if (log_control.log_entries[i].ldu_filep = + CONSOLE_OPEN("a+")) { + log_control.log_entries[i].log_type = K_LOG_CONSOLE; + log_control.log_entries[i].ldu_devname = "console"; + } + } + /* + * Is this a specification of a device? + */ + else if (!strncasecmp(cp, "DEVICE", 6)) { + /* + * We handle devices very similarly to files. + */ + if (cp[6] == '=') { + if (log_control.log_entries[i].ldu_filep = + DEVICE_OPEN(&cp[7], "w")) { + log_control.log_entries[i].log_type = K_LOG_DEVICE; + log_control.log_entries[i].ldu_devname = &cp[7]; + } + } + } + /* + * See if we successfully parsed this specification. + */ + if (log_control.log_entries[i].log_type == K_LOG_NONE) { + fprintf(stderr, lspec_parse_err_1, whoami, cp); + fprintf(stderr, lspec_parse_err_2, whoami); + } + else + ngood++; + } + } + /* + * If we didn't find anything, then free our lists. + */ + if (ngood == 0) { + for (i=0; ilog_type = K_LOG_SYSLOG; + log_control.log_entries->log_2free = (krb5_pointer) NULL; + log_control.log_entries->lsu_facility = LOG_AUTH; + log_control.log_entries->lsu_severity = LOG_ERR; + log_control.log_nentries = 1; + } + if (log_control.log_nentries) { + if (log_control.log_whoami = (char *) malloc(strlen(whoami)+1)) + strcpy(log_control.log_whoami, whoami); + if (log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN)) + gethostname(log_control.log_hostname, MAXHOSTNAMELEN); +#ifdef HAVE_OPENLOG + if (do_openlog) { + openlog(whoami, LOG_NDELAY|LOG_PID, log_facility); + log_control.log_opened = 1; + } +#endif /* HAVE_OPENLOG */ + if (do_com_err) + (void) set_com_err_hook(klog_com_err_proc); + } + return((log_control.log_nentries) ? 0 : ENOENT); +} + +/* + * krb5_klog_close() - Close the logging context and free all data. + */ +void +krb5_klog_close(kcontext) + krb5_context kcontext; +{ + int lindex; + (void) reset_com_err_hook(); + for (lindex = 0; lindex < log_control.log_nentries; lindex++) { + switch (log_control.log_entries[lindex].log_type) { + case K_LOG_FILE: + case K_LOG_STDERR: + /* + * Files/standard error. + */ + fclose(log_control.log_entries[lindex].lfu_filep); + break; + case K_LOG_CONSOLE: + case K_LOG_DEVICE: + /* + * Devices (may need special handling) + */ + DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep); + break; +#ifdef HAVE_SYSLOG + case K_LOG_SYSLOG: + /* + * System log. + */ + break; +#endif /* HAVE_SYSLOG */ + default: + break; + } + if (log_control.log_entries[lindex].log_2free) + free(log_control.log_entries[lindex].log_2free); + } + if (log_control.log_entries != &def_log_entry) + free(log_control.log_entries); + log_control.log_entries = (struct log_entry *) NULL; + log_control.log_nentries = 0; + if (log_control.log_whoami) + free(log_control.log_whoami); + log_control.log_whoami = (char *) NULL; + if (log_control.log_hostname) + free(log_control.log_hostname); + log_control.log_hostname = (char *) NULL; +#ifdef HAVE_CLOSELOG + if (log_control.log_opened) + closelog(); +#endif /* HAVE_CLOSELOG */ +} + +/* + * severity2string() - Convert a severity to a string. + */ +static char * +severity2string(severity) + int severity; +{ + int s; + const char *ss; + + s = severity & LOG_PRIMASK; + ss = log_ufo_string; + switch (s) { +#ifdef LOG_EMERG + case LOG_EMERG: + ss = log_emerg_string; + break; +#endif /* LOG_EMERG */ +#ifdef LOG_ALERT + case LOG_ALERT: + ss = log_alert_string; + break; +#endif /* LOG_ALERT */ +#ifdef LOG_CRIT + case LOG_CRIT: + ss = log_crit_string; + break; +#endif /* LOG_CRIT */ + case LOG_ERR: + ss = log_err_string; + break; +#ifdef LOG_WARNING + case LOG_WARNING: + ss = log_warning_string; + break; +#endif /* LOG_WARNING */ +#ifdef LOG_NOTICE + case LOG_NOTICE: + ss = log_notice_string; + break; +#endif /* LOG_NOTICE */ +#ifdef LOG_INFO + case LOG_INFO: + ss = log_info_string; + break; +#endif /* LOG_INFO */ +#ifdef LOG_DEBUG + case LOG_DEBUG: + ss = log_debug_string; + break; +#endif /* LOG_DEBUG */ + } + return((char *) ss); +} + +/* + * krb5_klog_syslog() - Simulate the calling sequence of syslog(3), while + * also performing the logging redirection as specified + * by krb5_klog_init(). + */ +static int +klog_vsyslog(priority, format, arglist) + int priority; + const char *format; + va_list arglist; +{ + char outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE]; + int lindex; + char *syslogp; + char *cp; + time_t now; +#ifdef HAVE_STRFTIME + size_t soff; +#endif /* HAVE_STRFTIME */ + + /* + * Format a syslog-esque message of the format: + * + * (verbose form) + * [](): + * + * (short form) + * + */ + cp = outbuf; + (void) time(&now); +#ifdef HAVE_STRFTIME + /* + * Format the date: mon dd hh:mm:ss + */ + soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now)); + if (soff > 0) + cp += soff; + else + return(-1); +#else /* HAVE_STRFTIME */ + /* + * Format the date: + * We ASSUME here that the output of ctime is of the format: + * dow mon dd hh:mm:ss tzs yyyy\n + * 012345678901234567890123456789 + */ + strncpy(outbuf, ctime(&now) + 4, 15); + cp += 15; +#endif /* HAVE_STRFTIME */ +#ifdef VERBOSE_LOGS + sprintf(cp, " %s %s[%d](%s): ", + log_control.log_hostname, log_control.log_whoami, getpid(), + severity2string(priority)); +#else + sprintf(cp, " "); +#endif + syslogp = &outbuf[strlen(outbuf)]; + + /* Now format the actual message */ +#ifdef HAVE_VSPRINTF + vsprintf(syslogp, format, arglist); +#else /* HAVE_VSPRINTF */ + sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1], + ((int *) arglist)[2], ((int *) arglist)[3], + ((int *) arglist)[4], ((int *) arglist)[5]); +#endif /* HAVE_VSPRINTF */ + + /* + * Now that we have the message formatted, perform the output to each + * logging specification. + */ + for (lindex = 0; lindex < log_control.log_nentries; lindex++) { + switch (log_control.log_entries[lindex].log_type) { + case K_LOG_FILE: + case K_LOG_STDERR: + /* + * Files/standard error. + */ + if (fprintf(log_control.log_entries[lindex].lfu_filep, + outbuf) < 0) { + /* Attempt to report error */ + fprintf(stderr, log_file_err, + log_control.log_entries[lindex].lfu_fname); + } + else { + fprintf(log_control.log_entries[lindex].lfu_filep, "\n"); + fflush(log_control.log_entries[lindex].lfu_filep); + } + break; + case K_LOG_CONSOLE: + case K_LOG_DEVICE: + /* + * Devices (may need special handling) + */ + if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep, + outbuf) < 0) { + /* Attempt to report error */ + fprintf(stderr, log_device_err, + log_control.log_entries[lindex].ldu_devname); + } + break; +#ifdef HAVE_SYSLOG + case K_LOG_SYSLOG: + /* + * System log. + */ + + /* Log the message with our header trimmed off */ + syslog(priority, syslogp); + break; +#endif /* HAVE_SYSLOG */ + default: + break; + } + } + return(0); +} + +#ifdef HAVE_STDARG_H +int +krb5_klog_syslog(int priority, const char *format, ...) +#else /* HAVE_STDARG_H */ +int +krb5_klog_syslog(priority, format, va_alist) + int priority; + const char *format; + va_dcl +#endif /* HAVE_STDARG_H */ +{ + int retval; + va_list pvar; + +#ifdef HAVE_STDARG_H + va_start(pvar, format); +#else /* HAVE_STDARG_H */ + va_start(pvar); +#endif /* HAVE_STDARG_H */ + retval = klog_vsyslog(priority, format, pvar); + va_end(pvar); + return(retval); +} +#endif /* !defined(_MSDOS) */ diff --git a/src/lib/kadm5/misc_free.c b/src/lib/kadm5/misc_free.c new file mode 100644 index 000000000..cfe28a411 --- /dev/null +++ b/src/lib/kadm5/misc_free.c @@ -0,0 +1,96 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif +#include +#include +#include "server_internal.h" + +kadm5_ret_t +kadm5_free_policy_ent(void *server_handle, kadm5_policy_ent_t val) +{ + kadm5_server_handle_t handle = server_handle; + + _KADM5_CHECK_HANDLE(server_handle); + + if(val) { + if (val->policy) + free(val->policy); + if (handle->api_version == KADM5_API_VERSION_1) + free(val); + } + return KADM5_OK; +} + +kadm5_ret_t + kadm5_free_name_list(void *server_handle, char **names, int count) +{ + _KADM5_CHECK_HANDLE(server_handle); + + while (count--) + free(names[count]); + free(names); +} + + +/* XXX this ought to be in libkrb5.a, but isn't */ +kadm5_ret_t krb5_free_key_data_contents(context, key) + krb5_context context; + krb5_key_data *key; +{ + int i, idx; + + idx = (key->key_data_ver == 1 ? 1 : 2); + for (i = 0; i < idx; i++) { + if (key->key_data_contents[i]) { + memset(key->key_data_contents[i], 0, key->key_data_length[i]); + free(key->key_data_contents[i]); + } + } +} + +kadm5_ret_t +kadm5_free_principal_ent(void *server_handle, + kadm5_principal_ent_t val) +{ + kadm5_server_handle_t handle = server_handle; + int i; + + _KADM5_CHECK_HANDLE(server_handle); + + if(val) { + if(val->principal) + krb5_free_principal(handle->context, val->principal); + if(val->mod_name) + krb5_free_principal(handle->context, val->mod_name); + if(val->policy) + free(val->policy); + if (handle->api_version > KADM5_API_VERSION_1) { + if (val->n_key_data) { + for (i = 0; i < val->n_key_data; i++) + krb5_free_key_data_contents(handle->context, + &val->key_data[i]); + free(val->key_data); + } + if (val->tl_data) { + krb5_tl_data *tl; + + while (val->tl_data) { + tl = val->tl_data->tl_data_next; + free(val->tl_data->tl_data_contents); + free(val->tl_data); + val->tl_data = tl; + } + } + } + + if (handle->api_version == KADM5_API_VERSION_1) + free(val); + } + return KADM5_OK; +} diff --git a/src/lib/kadm5/ovsec_glue.c b/src/lib/kadm5/ovsec_glue.c new file mode 100644 index 000000000..6118282df --- /dev/null +++ b/src/lib/kadm5/ovsec_glue.c @@ -0,0 +1,188 @@ +#define USE_KADM5_API_VERSION 1 +#include + +ovsec_kadm_ret_t ovsec_kadm_init_with_password(char *client_name, char *pass, + char *service_name, + char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return kadm5_init_with_password(client_name, pass, service_name, + realm, struct_version, api_version, + server_handle); +} + +ovsec_kadm_ret_t ovsec_kadm_init_with_skey(char *client_name, char *keytab, + char *service_name, + char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return kadm5_init_with_skey(client_name, keytab, service_name, realm, + struct_version, api_version, + server_handle); +} + +ovsec_kadm_ret_t ovsec_kadm_init(char *client_name, char *from_stash, + char *service_name, + char *realm, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return kadm5_init(client_name, from_stash, service_name, + realm, struct_version, api_version, + server_handle); +} + +ovsec_kadm_ret_t ovsec_kadm_destroy(void *server_handle) +{ + return kadm5_destroy(server_handle); +} + +ovsec_kadm_ret_t ovsec_kadm_flush(void *server_handle) +{ + return kadm5_flush(server_handle); +} + +ovsec_kadm_ret_t ovsec_kadm_create_principal(void *server_handle, + ovsec_kadm_principal_ent_t entry, + long mask, + char *password) +{ + return kadm5_create_principal(server_handle, + (kadm5_principal_ent_t) + entry, mask, password); +} + + +ovsec_kadm_ret_t ovsec_kadm_delete_principal(void *server_handle, + krb5_principal principal) +{ + return kadm5_delete_principal(server_handle, principal); +} + + +ovsec_kadm_ret_t ovsec_kadm_modify_principal(void *server_handle, + ovsec_kadm_principal_ent_t entry, + long mask) +{ + return kadm5_modify_principal(server_handle, + (kadm5_principal_ent_t) entry, mask); +} + + +ovsec_kadm_ret_t ovsec_kadm_rename_principal(void *server_handle, + krb5_principal source, + krb5_principal target) +{ + return kadm5_rename_principal(server_handle, source, target); +} + +ovsec_kadm_ret_t ovsec_kadm_get_principal(void *server_handle, + krb5_principal principal, + ovsec_kadm_principal_ent_t *entry) +{ + return kadm5_get_principal(server_handle, principal, + (kadm5_principal_ent_t *) entry); +} + +ovsec_kadm_ret_t ovsec_kadm_chpass_principal(void *server_handle, + krb5_principal principal, + char *password) +{ + return kadm5_chpass_principal(server_handle, principal, password); +} + +ovsec_kadm_ret_t ovsec_kadm_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret) +{ + return kadm5_chpass_principal_util(server_handle, princ, new_pw, + ret_pw, msg_ret); +} + +ovsec_kadm_ret_t ovsec_kadm_randkey_principal(void *server_handle, + krb5_principal principal, + krb5_keyblock **key) +{ + return kadm5_randkey_principal(server_handle, principal, key); +} + +ovsec_kadm_ret_t ovsec_kadm_create_policy(void *server_handle, + ovsec_kadm_policy_ent_t entry, + long mask) +{ + return kadm5_create_policy(server_handle, + (kadm5_policy_ent_t) entry, mask); +} + +ovsec_kadm_ret_t ovsec_kadm_delete_policy(void *server_handle, + ovsec_kadm_policy_t name) +{ + return kadm5_delete_policy(server_handle, (kadm5_policy_t) name); +} + +ovsec_kadm_ret_t ovsec_kadm_modify_policy(void *server_handle, + ovsec_kadm_policy_ent_t entry, + long mask) +{ + return kadm5_modify_policy(server_handle, + (kadm5_policy_ent_t) entry, mask); +} + + +ovsec_kadm_ret_t ovsec_kadm_get_policy(void *server_handle, + ovsec_kadm_policy_t name, + ovsec_kadm_policy_ent_t *entry) +{ + return kadm5_get_policy(server_handle, (kadm5_policy_t) name, + (kadm5_policy_ent_t *) entry); +} + + +ovsec_kadm_ret_t ovsec_kadm_free_policy_ent(void *server_handle, + ovsec_kadm_policy_ent_t val) +{ + return kadm5_free_policy_ent(server_handle, (kadm5_policy_ent_t) val); +} + +ovsec_kadm_ret_t ovsec_kadm_free_name_list(void *server_handle, + char **names, int count) +{ + return kadm5_free_name_list(server_handle, names, count); +} + +ovsec_kadm_ret_t +ovsec_kadm_free_principal_ent(void *server_handle, + ovsec_kadm_principal_ent_t val) +{ + return kadm5_free_principal_ent(server_handle, + (kadm5_principal_ent_t) val); +} + +ovsec_kadm_ret_t ovsec_kadm_get_privs(void *server_handle, long *privs) +{ + return kadm5_get_privs(server_handle, privs); +} + +ovsec_kadm_ret_t ovsec_kadm_get_principals(void *server_handle, + char *exp, + char ***princs, + int *count) +{ + return kadm5_get_principals(server_handle, exp, princs, count); +} + +ovsec_kadm_ret_t ovsec_kadm_get_policies(void *server_handle, + char *exp, + char ***pols, + int *count) +{ + return kadm5_get_policies(server_handle, exp, pols, count); +} + diff --git a/src/lib/kadm5/server_acl.c b/src/lib/kadm5/server_acl.c new file mode 100644 index 000000000..16a7f4e40 --- /dev/null +++ b/src/lib/kadm5/server_acl.c @@ -0,0 +1,511 @@ +/* + * kadmin/v5server/srv_acl.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. + * + */ + +/* + * srv_acl.c - Handle Kerberos ACL related functions. + */ +#include +#include +#include +#include "k5-int.h" +#include "server_acl.h" +#include + +typedef struct _acl_op_table { + char ao_op; + krb5_int32 ao_mask; +} aop_t; + +typedef struct _acl_entry { + struct _acl_entry *ae_next; + char *ae_name; + krb5_boolean ae_name_bad; + krb5_principal ae_principal; + krb5_int32 ae_op_allowed; + char *ae_target; + krb5_boolean ae_target_bad; + krb5_principal ae_target_princ; +} aent_t; + +static const aop_t acl_op_table[] = { + { 'a', ACL_ADD }, + { 'd', ACL_DELETE }, + { 'm', ACL_MODIFY }, + { 'c', ACL_CHANGEPW }, + { 'i', ACL_INQUIRE }, + { 'l', ACL_LIST }, + { 'x', ACL_ALL_MASK }, + { '*', ACL_ALL_MASK }, + { '\0', 0 } +}; + +static aent_t *acl_list_head = (aent_t *) NULL; +static aent_t *acl_list_tail = (aent_t *) NULL; + +static const char *acl_acl_file = (char *) NULL; +static int acl_inited = 0; +static int acl_debug_level = 0; +/* + * This is the catchall entry. If nothing else appropriate is found, or in + * the case where the ACL file is not present, this entry controls what can + * be done. + */ +static const char *acl_catchall_entry = NULL; + +static const char *acl_line2long_msg = "%s: line %d too long, truncated\n"; +static const char *acl_op_bad_msg = "Unrecognized ACL operation '%c' in %s\n"; +static const char *acl_syn_err_msg = "%s: syntax error at line %d <%10s...>\n"; +static const char *acl_cantopen_msg = "\007cannot open ACL file"; + +/* + * acl_get_line() - Get a line from the ACL file. + */ +static char * +acl_get_line(fp, lnp) + FILE *fp; + int *lnp; +{ + int i, domore; + static char acl_buf[BUFSIZ]; + + for (domore = 1; domore && !feof(fp); ) { + /* Copy in the line */ + for (i=0; + ((i + * [ ] + */ + acle = (aent_t *) NULL; + acle_object[0] = '\0'; + nmatch = sscanf(lp, "%s %s %s", acle_principal, acle_ops, acle_object); + if (nmatch >= 2) { + acle = (aent_t *) malloc(sizeof(aent_t)); + if (acle) { + acle->ae_next = (aent_t *) NULL; + acle->ae_op_allowed = (krb5_int32) 0; + acle->ae_target = + (nmatch >= 3) ? strdup(acle_object) : (char *) NULL; + acle->ae_target_bad = 0; + acle->ae_target_princ = (krb5_principal) NULL; + opok = 1; + for (op=acle_ops; *op; op++) { + char rop; + + rop = (isupper(*op)) ? tolower(*op) : *op; + found = 0; + for (t=0; acl_op_table[t].ao_op; t++) { + if (rop == acl_op_table[t].ao_op) { + found = 1; + if (rop == *op) + acle->ae_op_allowed |= acl_op_table[t].ao_mask; + else + acle->ae_op_allowed &= ~acl_op_table[t].ao_mask; + } + } + if (!found) { + fprintf(stderr, acl_op_bad_msg, *op, lp); + opok = 0; + } + } + if (opok) { + acle->ae_name = (char *) malloc(strlen(acle_principal)+1); + if (acle->ae_name) { + strcpy(acle->ae_name, acle_principal); + acle->ae_principal = (krb5_principal) NULL; + acle->ae_name_bad = 0; + DPRINT(DEBUG_ACL, acl_debug_level, + ("A ACL entry %s -> opmask %x\n", + acle->ae_name, acle->ae_op_allowed)); + } + else { + if (acle->ae_target) + free(acle->ae_target); + free(acle); + acle = (aent_t *) NULL; + } + } + else { + if (acle->ae_target) + free(acle->ae_target); + free(acle); + acle = (aent_t *) NULL; + } + } + } + DPRINT(DEBUG_CALLS, acl_debug_level, + ("X acl_parse_line() = %x\n", (long) acle)); + return(acle); +} + +/* + * acl_free_entries() - Free all ACL entries. + */ +static void +acl_free_entries() +{ + aent_t *ap; + aent_t *np; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n")); + for (ap=acl_list_head; ap; ap = np) { + if (ap->ae_name) + free(ap->ae_name); + if (ap->ae_principal) + krb5_free_principal((krb5_context) NULL, ap->ae_principal); + if (ap->ae_target) + free(ap->ae_target); + if (ap->ae_target_princ) + krb5_free_principal((krb5_context) NULL, ap->ae_target_princ); + np = ap->ae_next; + free(ap); + } + acl_list_head = acl_list_tail = (aent_t *) NULL; + acl_inited = 0; + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n")); +} + +/* + * acl_load_acl_file() - Open and parse the ACL file. + */ +static int +acl_load_acl_file() +{ +char tmpbuf[10]; + FILE *afp; + char *alinep; + aent_t **aentpp; + int alineno; + int retval = 1; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n")); + /* Open the ACL file for read */ + if (afp = fopen(acl_acl_file, "r")) { + alineno = 1; + aentpp = &acl_list_head; + + /* Get a non-comment line */ + while (alinep = acl_get_line(afp, &alineno)) { + /* Parse it */ + *aentpp = acl_parse_line(alinep); + /* If syntax error, then fall out */ + if (!*aentpp) { + fprintf(stderr, acl_syn_err_msg, + acl_acl_file, alineno, alinep); + retval = 0; + break; + } + acl_list_tail = *aentpp; + aentpp = &(*aentpp)->ae_next; + } + + if (acl_catchall_entry) { + strcpy(tmpbuf, acl_catchall_entry); + if (*aentpp = acl_parse_line(tmpbuf)) { + acl_list_tail = *aentpp; + } + else { + retval = 0; + DPRINT(DEBUG_OPERATION, acl_debug_level, + ("> catchall acl entry (%s) load failed\n", + acl_catchall_entry)); + } + fclose(afp); + } + } + else { + com_err(acl_acl_file, errno, acl_cantopen_msg); + if (acl_list_head = acl_parse_line(acl_catchall_entry)) { + acl_list_tail = acl_list_head; + } + else { + retval = 0; + DPRINT(DEBUG_OPERATION, acl_debug_level, + ("> catchall acl entry (%s) load failed\n", + acl_catchall_entry)); + } + } + + if (!retval) { + acl_free_entries(); + } + DPRINT(DEBUG_CALLS, acl_debug_level, + ("X acl_load_acl_file() = %d\n", retval)); + return(retval); +} + +/* + * acl_match_data() - See if two data entries match. + * + * Wildcarding is only supported for a whole component. + */ +static krb5_boolean +acl_match_data(e1, e2) + krb5_data *e1, *e2; +{ + krb5_boolean retval; + + DPRINT(DEBUG_CALLS, acl_debug_level, + ("* acl_match_entry(%s, %s)\n", e1->data, e2->data)); + retval = 0; + if (!strncmp(e1->data, "*", e1->length) || + !strncmp(e2->data, "*", e2->length)) { + retval = 1; + } + else { + if ((e1->length == e2->length) && + (!strncmp(e1->data, e2->data, e1->length))) + retval = 1; + } + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval)); + return(retval); +} + +/* + * acl_find_entry() - Find a matching entry. + */ +static aent_t * +acl_find_entry(kcontext, principal, dest_princ) + krb5_context kcontext; + krb5_principal principal; + krb5_principal dest_princ; +{ + aent_t *entry; + krb5_error_code kret; + int i; + int matchgood; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n")); + for (entry=acl_list_head; entry; entry = entry->ae_next) { + if (!strcmp(entry->ae_name, "*")) { + DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n")); + break; + } + if (!entry->ae_principal && !entry->ae_name_bad) { + kret = krb5_parse_name(kcontext, + entry->ae_name, + &entry->ae_principal); + if (kret) + entry->ae_name_bad = 1; + } + if (entry->ae_name_bad) { + DPRINT(DEBUG_ACL, acl_debug_level, + ("A Bad ACL entry %s\n", entry->ae_name)); + continue; + } + if (entry->ae_target && + !entry->ae_target_princ && + !entry->ae_target_bad) { + kret = krb5_parse_name(kcontext, + entry->ae_target, + &entry->ae_target_princ); + if (kret) + entry->ae_target_bad = 1; + } + if (entry->ae_target_bad) { + DPRINT(DEBUG_ACL, acl_debug_level, + ("A Bad target in an ACL entry for %s\n", entry->ae_name)); + entry->ae_name_bad = 1; + continue; + } + matchgood = 0; + if (acl_match_data(&entry->ae_principal->realm, + &principal->realm) && + (entry->ae_principal->length == principal->length)) { + matchgood = 1; + for (i=0; ilength; i++) { + if (!acl_match_data(&entry->ae_principal->data[i], + &principal->data[i])) { + matchgood = 0; + break; + } + } + } + if (!matchgood) + continue; + + /* We've matched the principal. If we have a target, then try it */ + if (entry->ae_target && entry->ae_target_princ && dest_princ) { + if (acl_match_data(&entry->ae_target_princ->realm, + &dest_princ->realm) && + (entry->ae_target_princ->length == dest_princ->length)) { + for (i=0; ilength; i++) { + if (!acl_match_data(&entry->ae_target_princ->data[i], + &dest_princ->data[i])) { + matchgood = 0; + break; + } + } + } + else + matchgood = 0; + } + + if (matchgood) + break; + } + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry)); + return(entry); +} + +/* + * acl_init() - Initialize ACL context. + */ +krb5_error_code +acl_init(kcontext, debug_level, acl_file) + krb5_context kcontext; + int debug_level; + char *acl_file; +{ + krb5_error_code kret; + + kret = 0; + acl_debug_level = debug_level; + DPRINT(DEBUG_CALLS, acl_debug_level, + ("* acl_init(afile=%s)\n", + ((acl_file) ? acl_file : "(null)"))); + acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL; + acl_inited = acl_load_acl_file(); + + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret)); + return(kret); +} + +/* + * acl_finish - Terminate ACL context. + */ +void +acl_finish(kcontext, debug_level) + krb5_context kcontext; + int debug_level; +{ + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n")); + acl_free_entries(); + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n")); +} + +/* + * acl_op_permitted() - Is this operation permitted for this principal? + * this code used not to be based on gssapi. In order + * to minimize porting hassles, I've put all the + * gssapi hair in this function. This might not be + * the best medium-term solution. (The best long-term + * solution is, of course, a real authorization service.) + */ +krb5_boolean +acl_check(kcontext, caller, opmask, principal) + krb5_context kcontext; + gss_name_t caller; + krb5_int32 opmask; + krb5_principal principal; +{ + krb5_boolean retval; + aent_t *aentry; + gss_buffer_desc caller_buf; + gss_OID caller_oid; + OM_uint32 emaj, emin; + krb5_error_code code; + krb5_principal caller_princ; + + DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n")); + + if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf, + &caller_oid))) + return(0); + + code = krb5_parse_name(kcontext, (char *) caller_buf.value, + &caller_princ); + + gss_release_buffer(&emin, &caller_buf); + + if (code) + return(code); + + retval = 0; + if (aentry = acl_find_entry(kcontext, caller_princ, principal)) { + if ((aentry->ae_op_allowed & opmask) == opmask) + retval = 1; + } + + krb5_free_principal(kcontext, caller_princ); + + DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n", + retval)); + return(retval); +} + +kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + /* this is impossible to do with the current interface. For now, + return all privs, which will confuse some clients, but not + deny any access to users of "smart" clients which try to cache */ + + *privs = ~0; + + return KADM5_OK; +} diff --git a/src/lib/kadm5/server_acl.h b/src/lib/kadm5/server_acl.h new file mode 100644 index 000000000..9dfc8daba --- /dev/null +++ b/src/lib/kadm5/server_acl.h @@ -0,0 +1,81 @@ +/* + * kadmin/v5server/kadm5_defs.h + * + * 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. + * + */ + +#ifndef SERVER_ACL_H__ +#define SERVER_ACL_H__ + +/* + * Debug definitions. + */ +#define DEBUG_SPROC 1 +#define DEBUG_OPERATION 2 +#define DEBUG_HOST 4 +#define DEBUG_REALM 8 +#define DEBUG_REQUESTS 16 +#define DEBUG_ACL 32 +#define DEBUG_PROTO 64 +#define DEBUG_CALLS 128 +#define DEBUG_NOSLAVES 256 +#ifdef DEBUG +#define DPRINT(l1, cl, al) if ((cl & l1) != 0) xprintf al +#else /* DEBUG */ +#define DPRINT(l1, cl, al) +#endif /* DEBUG */ +#define DLOG(l1, cl, msg) if ((cl & l1) != 0) \ + com_err(programname, 0, msg) + +/* + * Access control bits. + */ +#define ACL_ADD 1 +#define ACL_DELETE 2 +#define ACL_MODIFY 4 +#define ACL_CHANGEPW 8 +/* #define ACL_CHANGE_OWN_PW 16 */ +#define ACL_INQUIRE 32 +/* #define ACL_EXTRACT 64 */ +#define ACL_LIST 128 +#define ACL_RENAME (ACL_ADD+ACL_DELETE) + +#define ACL_ALL_MASK (ACL_ADD | \ + ACL_DELETE | \ + ACL_MODIFY | \ + ACL_CHANGEPW | \ + ACL_INQUIRE | \ + ACL_LIST) + +krb5_error_code acl_init + KRB5_PROTOTYPE((krb5_context, + int, + char *)); +void acl_finish + KRB5_PROTOTYPE((krb5_context, + int)); +krb5_boolean acl_check + KRB5_PROTOTYPE((krb5_context, + gss_name_t, + krb5_int32, + krb5_principal)); + +#endif /* SERVER_ACL_H__ */ diff --git a/src/lib/kadm5/server_dict.c b/src/lib/kadm5/server_dict.c new file mode 100644 index 000000000..6c0bcef03 --- /dev/null +++ b/src/lib/kadm5/server_dict.c @@ -0,0 +1,199 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "server_internal.h" + +static char **word_list = NULL; /* list of word pointers */ +static char *word_block = NULL; /* actual word data */ +static int word_count = 0; /* number of words */ +extern int errno; + +/* + * Function: word_compare + * + * Purpose: compare two words in the dictionary. + * + * Arguments: + * w1 (input) pointer to first word + * w2 (input) pointer to second word + * result of strcmp + * + * Requires: + * w1 and w2 to point to valid memory + * + */ + +static int +word_compare(const void *s1, const void *s2) +{ + return (strcasecmp(*(char **)s1, *(char **)s2)); +} + +/* + * Function: init-dict + * + * Purpose: Initialize in memory word dictionary + * + * Arguments: + * none + * KADM5_OK on sucsess errno on failure; + * (but success on ENOENT) + * + * Requires: + * If WORDFILE exists, it must contain a list of words, + * one word per-line. + * + * Effects: + * If WORDFILE exists, it is read into memory sorted for future + * use. If it does not exist, it syslogs an error message and returns + * success. + * + * Modifies: + * word_list to point to a chunck of allocated memory containing + * pointers to words + * word_block to contain the dictionary. + * + */ + +int init_dict(kadm5_config_params *params) +{ + int fd, + len, + i; + char *p, + *t; + struct stat sb; + + if(word_list != NULL && word_block != NULL) + return KADM5_OK; + if (! (params->mask & KADM5_CONFIG_DICT_FILE)) { + syslog(LOG_INFO, "No dictionary file specified, continuing " + "without one."); + return KADM5_OK; + } + if ((fd = open(params->dict_file, O_RDONLY)) == -1) { + if (errno == ENOENT) { + syslog(LOG_ERR, "WARNING! Cannot find dictionary file %s, " + "continuing without one.", params->dict_file); + return KADM5_OK; + } else + return errno; + } + if (fstat(fd, &sb) == -1) + return errno; + if ((word_block = (char *) malloc(sb.st_size + 1)) == NULL) + return errno; + if (read(fd, word_block, sb.st_size) != sb.st_size) + return errno; + (void) close(fd); + word_block[sb.st_size] = '\0'; + + p = word_block; + len = sb.st_size; + while(len > 0 && (t = memchr(p, '\n', len)) != NULL) { + *t = '\0'; + len -= t - p + 1; + p = t + 1; + word_count++; + } + if ((word_list = (char **) malloc(word_count * sizeof(char *))) == NULL) + return errno; + p = word_block; + for (i = 0; i < word_count; i++) { + word_list[i] = p; + p += strlen(p) + 1; + } + qsort(word_list, word_count, sizeof(char *), word_compare); + return KADM5_OK; +} + +/* + * Function: find_word + * + * Purpose: See if the specified word exists in the in-core dictionary + * + * Arguments: + * word (input) word to search for. + * WORD_NOT_FOUND if not in dictionary, + * KADM5_OK if if found word + * errno if init needs to be called and returns an + * error + * + * Requires: + * word to be a null terminated string. + * That word_list and word_block besetup + * + * Effects: + * finds word in dictionary. + * Modifies: + * nothing. + * + */ + +int +find_word(const char *word) +{ + char **value; + + if(word_list == NULL || word_block == NULL) + return WORD_NOT_FOUND; + if ((value = (char **) bsearch(&word, word_list, word_count, sizeof(char *), + word_compare)) == NULL) + return WORD_NOT_FOUND; + else + return KADM5_OK; +} + +/* + * Function: destroy_dict + * + * Purpose: destroy in-core copy of dictionary. + * + * Arguments: + * none + * none + * Requires: + * nothing + * Effects: + * frees up memory occupied by word_list and word_block + * sets count back to 0, and resets the pointers to NULL + * + * Modifies: + * word_list, word_block, and word_count. + * + */ + +void +destroy_dict(void) +{ + if(word_list) { + free(word_list); + word_list = NULL; + } + if(word_block) { + free(word_block); + word_block = NULL; + } + if(word_count) + word_count = 0; + return; +} diff --git a/src/lib/kadm5/server_handle.c b/src/lib/kadm5/server_handle.c new file mode 100644 index 000000000..53abe94dd --- /dev/null +++ b/src/lib/kadm5/server_handle.c @@ -0,0 +1,9 @@ +#include +#include +#include "server_internal.h" + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/server_init.c b/src/lib/kadm5/server_init.c new file mode 100644 index 000000000..653f6d896 --- /dev/null +++ b/src/lib/kadm5/server_init.c @@ -0,0 +1,330 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include "server_internal.h" + +/* + * Function check_handle + * + * Purpose: Check a server handle and return a com_err code if it is + * invalid or 0 if it is valid. + * + * Arguments: + * + * handle The server handle. + */ + +static int check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} + +kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + return kadm5_init(client_name, pass, service_name, params, + struct_version, api_version, + server_handle); +} + +kadm5_ret_t kadm5_init_with_creds(char *client_name, + krb5_ccache ccache, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + /* + * A program calling init_with_creds *never* expects to prompt the + * user. Therefore, always pass a dummy password in case this is + * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and + * MKEY_FROM_KBD is non-zero, return an error. + */ + if (api_version == KADM5_API_VERSION_2 && params && + (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && + params->mkey_from_kbd) + return KADM5_BAD_SERVER_PARAMS; + return kadm5_init(client_name, NULL, service_name, params, + struct_version, api_version, + server_handle); +} + + +kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, + char *service_name, + kadm5_config_params *params, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + /* + * A program calling init_with_skey *never* expects to prompt the + * user. Therefore, always pass a dummy password in case this is + * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and + * MKEY_FROM_KBD is non-zero, return an error. + */ + if (api_version == KADM5_API_VERSION_2 && params && + (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && + params->mkey_from_kbd) + return KADM5_BAD_SERVER_PARAMS; + return kadm5_init(client_name, NULL, service_name, params, + struct_version, api_version, + server_handle); +} + +kadm5_ret_t kadm5_init(char *client_name, char *pass, + char *service_name, + kadm5_config_params *params_in, + krb5_ui_4 struct_version, + krb5_ui_4 api_version, + void **server_handle) +{ + int ret; + kadm5_server_handle_t handle; + kadm5_config_params params_local; /* for v1 compat */ + + if (! server_handle) + return EINVAL; + + if (! client_name) + return EINVAL; + + if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle))) + return ENOMEM; + memset(handle, 0, sizeof(*handle)); + + if (ret = (int) krb5_init_context(&(handle->context))) { + free(handle); + return(ret); + } + + initialize_ovk_error_table(); + initialize_adb_error_table(); + initialize_ovku_error_table(); + krb5_init_ets(handle->context); + + handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; + handle->struct_version = struct_version; + handle->api_version = api_version; + + /* + * Verify the version numbers before proceeding; we can't use + * CHECK_HANDLE because not all fields are set yet. + */ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, + KADM5_NEW_SERVER_API_VERSION); + + /* + * Acquire relevant profile entries. In version 2, merge values + * in params_in with values from profile, based on + * params_in->mask. + * + * In version 1, we've given a realm (which may be NULL) instead + * of params_in. So use that realm, make params_in contain an + * empty mask, and behave like version 2. + */ + memset((char *) ¶ms_local, 0, sizeof(params_local)); + if (api_version == KADM5_API_VERSION_1) { + params_local.realm = (char *) params_in; + if (params_in) + params_local.mask = KADM5_CONFIG_REALM; + params_in = ¶ms_local; + } + +#define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER) + if (params_in && (params_in->mask & ILLEGAL_PARAMS)) { + krb5_free_context(handle->context); + free(handle); + return KADM5_BAD_SERVER_PARAMS; + } + + if (ret = kadm5_get_config_params(handle->context, + (char *) NULL, + (char *) NULL, + params_in, + &handle->params)) { + krb5_free_context(handle->context); + free(handle); + return(ret); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \ + KADM5_CONFIG_ADBNAME | \ + KADM5_CONFIG_ADB_LOCKFILE | \ + KADM5_CONFIG_ENCTYPE | \ + KADM5_CONFIG_FLAGS | \ + KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \ + KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) + + if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_free_context(handle->context); + free(handle); + return KRB5_CONFIG_BADFORMAT; + } + + /* + * Set the db_name based on configuration before calling + * krb5_db_init, so it will get used. + */ + if (ret = krb5_dbm_db_set_name(handle->context, + handle->params.dbname)) { + free(handle); + return(ret); + } + + if (ret = krb5_db_init(handle->context)) { + krb5_free_context(handle->context); + free(handle); + return(ret); + } + + if ((ret = krb5_parse_name(handle->context, client_name, + &handle->current_caller))) { + krb5_db_fini(handle->context); + krb5_free_context(handle->context); + free(handle); + return ret; + } + + if (! (handle->lhandle = malloc(sizeof(*handle)))) { + krb5_db_fini(handle->context); + krb5_free_context(handle->context); + free(handle); + return ENOMEM; + } + *handle->lhandle = *handle; + handle->lhandle->api_version = KADM5_API_VERSION_2; + handle->lhandle->struct_version = KADM5_STRUCT_VERSION; + handle->lhandle->lhandle = handle->lhandle; + + /* can't check the handle until current_caller is set */ + if (ret = check_handle((void *) handle)) { + free(handle); + return ret; + } + + /* + * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL + * or an empty string, reads the master password from [the stash + * file]. Otherwise, the non-NULL password is ignored and the + * user is prompted for it via the tty." However, the code was + * implemented the other way: when a non-NULL password was + * provided, the stash file was used. This is somewhat more + * sensible, as then a local or remote client that provides a + * password does not prompt the user. This code maintains the + * previous actual behavior, and not the old spec behavior, + * because that is how the unit tests are written. + * + * In KADM5_API_VERSION_2, this decision is controlled by + * params. + * + * kdb_init_master's third argument is "from_keyboard". + */ + if (ret = kdb_init_master(handle, handle->params.realm, + (handle->api_version == KADM5_API_VERSION_1 ? + ((pass == NULL) || !(strlen(pass))) : + ((handle->params.mask & + KADM5_CONFIG_MKEY_FROM_KBD) && + handle->params.mkey_from_kbd)) + )) { + krb5_db_fini(handle->context); + krb5_free_context(handle->context); + free(handle); + return ret; + } + + if ((ret = kdb_init_hist(handle, handle->params.realm))) { + krb5_db_fini(handle->context); + krb5_free_context(handle->context); + free(handle); + return ret; + } + + if (ret = init_dict(&handle->params)) { + krb5_db_fini(handle->context); + krb5_free_principal(handle->context, handle->current_caller); + krb5_free_context(handle->context); + free(handle); + return ret; + } + + if (ret = adb_policy_init(handle)) { + krb5_db_fini(handle->context); + krb5_free_principal(handle->context, handle->current_caller); + krb5_free_context(handle->context); + free(handle); + return ret; + } + handle->lhandle->policy_db = handle->policy_db; + + *server_handle = (void *) handle; + + return KADM5_OK; +} + +kadm5_ret_t kadm5_destroy(void *server_handle) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + destroy_dict(); + + adb_policy_close(handle); + krb5_db_fini(handle->context); + krb5_free_principal(handle->context, handle->current_caller); + krb5_free_context(handle->context); + handle->magic_number = 0; + free(handle->lhandle); + free(handle); + + return KADM5_OK; +} + +kadm5_ret_t kadm5_flush(void *server_handle) +{ + kadm5_server_handle_t handle = server_handle; + kadm5_ret_t ret; + + CHECK_HANDLE(server_handle); + + if ((ret = krb5_db_fini(handle->context)) || + /* + * Set the db_name based on configuration before calling + * krb5_db_init, so it will get used. + */ + (ret = krb5_dbm_db_set_name(handle->context, + handle->params.dbname)) || + (ret = krb5_db_init(handle->context)) || + (ret = adb_policy_close(handle)) || + (ret = adb_policy_init(handle))) { + (void) kadm5_destroy(server_handle); + return ret; + } + return KADM5_OK; +} + +int _kadm5_check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} diff --git a/src/lib/kadm5/server_internal.h b/src/lib/kadm5/server_internal.h new file mode 100644 index 000000000..415f6d7d7 --- /dev/null +++ b/src/lib/kadm5/server_internal.h @@ -0,0 +1,103 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +/* + * This header file is used internally by the Admin API server + * libraries and Admin server. IF YOU THINK YOU NEED TO USE THIS FILE + * FOR ANYTHING, YOU'RE ALMOST CERTAINLY WRONG. + */ + +#ifndef __KADM5_SERVER_INTERNAL_H__ +#define __KADM5_SERVER_INTERNAL_H__ + +#include +#include +#include "k5-int.h" +#include +#include +#include "admin_internal.h" +#include "adb.h" + +typedef struct _kadm5_server_handle_t { + krb5_ui_4 magic_number; + krb5_ui_4 struct_version; + krb5_ui_4 api_version; + krb5_context context; + krb5_principal current_caller; + kadm5_config_params params; + struct _kadm5_server_handle_t *lhandle; + osa_adb_policy_t policy_db; +} kadm5_server_handle_rec, *kadm5_server_handle_t; + +kadm5_ret_t adb_policy_init(kadm5_server_handle_t handle); +kadm5_ret_t adb_policy_close(kadm5_server_handle_t handle); +kadm5_ret_t passwd_check(kadm5_server_handle_t handle, + char *pass, int use_policy, + kadm5_policy_ent_t policy, + krb5_principal principal); +kadm5_ret_t principal_exists(krb5_principal principal); +krb5_error_code kdb_init_master(kadm5_server_handle_t handle, + char *r, int from_keyboard); +krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, + char *r); +krb5_error_code kdb_get_entry(kadm5_server_handle_t handle, + krb5_principal principal, krb5_db_entry *kdb, + osa_princ_ent_rec *adb); +krb5_error_code kdb_free_entry(kadm5_server_handle_t handle, + krb5_db_entry *kdb, osa_princ_ent_rec *adb); +krb5_error_code kdb_put_entry(kadm5_server_handle_t handle, + krb5_db_entry *kdb, osa_princ_ent_rec *adb); +krb5_error_code kdb_delete_entry(kadm5_server_handle_t handle, + krb5_principal name); + +int init_dict(kadm5_config_params *); +int find_word(const char *word); +void destroy_dict(void); + +/* + * *Warning* + * *Warning* This is going to break if we + * *Warning* ever go multi-threaded + * *Warning* + */ +extern krb5_principal current_caller; + +/* + * Why is this (or something similar) not defined *anywhere* in krb5? + */ +#define KSUCCESS 0 +#define WORD_NOT_FOUND 1 + +/* + * all the various mask bits or'd together + */ + +#define ALL_PRINC_MASK (KADM5_PRINCIPAL | KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | \ + KADM5_LAST_PWD_CHANGE | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | \ + KADM5_MOD_TIME | KADM5_MOD_NAME | KADM5_KVNO | KADM5_MKVNO | \ + KADM5_AUX_ATTRIBUTES | KADM5_POLICY_CLR | KADM5_POLICY) + +#define ALL_POLICY_MASK (KADM5_POLICY | KADM5_PW_MAX_LIFE | KADM5_PW_MIN_LIFE | \ + KADM5_PW_MIN_LENGTH | KADM5_PW_MIN_CLASSES | KADM5_PW_HISTORY_NUM | \ + KADM5_REF_COUNT) + +#define SERVER_CHECK_HANDLE(handle) \ +{ \ + kadm5_server_handle_t srvr = \ + (kadm5_server_handle_t) handle; \ + \ + if (! srvr->current_caller) \ + return KADM5_BAD_SERVER_HANDLE; \ + if (! srvr->lhandle) \ + return KADM5_BAD_SERVER_HANDLE; \ +} + +#define CHECK_HANDLE(handle) \ + GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, \ + KADM5_NEW_SERVER_API_VERSION) \ + SERVER_CHECK_HANDLE(handle) + +#endif /* __KADM5_SERVER_INTERNAL_H__ */ diff --git a/src/lib/kadm5/server_kdb.c b/src/lib/kadm5/server_kdb.c new file mode 100644 index 000000000..1a900a380 --- /dev/null +++ b/src/lib/kadm5/server_kdb.c @@ -0,0 +1,424 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include "k5-int.h" +#include +#include "server_internal.h" + +krb5_principal master_princ; +krb5_encrypt_block master_encblock; +krb5_keyblock master_keyblock; +krb5_db_entry master_db; + +krb5_principal hist_princ; +krb5_encrypt_block hist_encblock; +krb5_keyblock hist_key; +krb5_db_entry hist_db; +krb5_kvno hist_kvno; + +/* much of this code is stolen from the kdc. there should be some + library code to deal with this. */ + +krb5_error_code kdb_init_master(kadm5_server_handle_t handle, + char *r, int from_keyboard) +{ + int ret = 0; + char *realm; + krb5_keyblock tmk; + + if (r == NULL) { + if ((ret = krb5_get_default_realm(handle->context, &realm))) + return ret; + } else { + realm = r; + } + + if ((ret = krb5_db_setup_mkey_name(handle->context, + handle->params.mkey_name, + realm, NULL, &master_princ))) + goto done; + + master_keyblock.enctype = handle->params.enctype; + + krb5_use_enctype(handle->context, &master_encblock, + master_keyblock.enctype); + + if (ret = krb5_db_fetch_mkey(handle->context, master_princ, + &master_encblock, from_keyboard, + FALSE /* only prompt once */, + handle->params.stash_file, + NULL /* I'm not sure about this, + but it's what the kdc does --marc */, + &master_keyblock)) + goto done; + + if ((ret = krb5_db_init(handle->context)) != KSUCCESS) + goto done; + + if ((ret = krb5_db_verify_master_key(handle->context, master_princ, + &master_keyblock, + &master_encblock))) { + krb5_db_fini(handle->context); + return ret; + } + + /* the kdc gets the db mkvno here. The admin server never uses this + bit of information, so there's no reason to retrieve it. */ + + if ((ret = krb5_process_key(handle->context, &master_encblock, + &master_keyblock))) { + krb5_db_fini(handle->context); + goto done; + } + +done: + if (r == NULL) + free(realm); + + return(ret); +} + +/* + * Function: kdb_init_hist + * + * Purpose: Initializes the global history variables. + * + * Arguments: + * + * handle (r) kadm5 api server handle + * r (r) realm of history principal to use, or NULL + * + * Effects: This function sets the value of the following global + * variables: + * + * hist_princ krb5_principal holding the history principal + * hist_db krb5_db_entry of the history principal + * hist_key krb5_keyblock holding the history principal's key + * hist_encblock krb5_encrypt_block holding the procssed hist_key + * hist_kvno the version number of the history key + * + * If the history principal does not already exist, this function + * attempts to create it with kadm5_create_principal. WARNING! + * If the history principal is deleted and this function is executed + * (by kadmind, or kadmin.local, or anything else with permission), + * the principal will be assigned a new random key and all existing + * password history information will become useless. + */ +krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r) +{ + int ret = 0; + char *realm, *hist_name; + krb5_key_data *key_data; + + if (r == NULL) { + if ((ret = krb5_get_default_realm(handle->context, &realm))) + return ret; + } else { + realm = r; + } + + if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) + + strlen(realm) + 2)) == NULL) + goto done; + + (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm); + + if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ))) + goto done; + + if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) { + kadm5_principal_ent_rec ent; + + if (ret != KADM5_UNK_PRINC) + goto done; + + /* try to create the principal */ + + memset(&ent, 0, sizeof(ent)); + + ent.principal = hist_princ; + ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX; + ent.attributes = 0; + + /* this uses hist_kvno. So we set it to 2, which will be the + correct value once the principal is created and randomized. + Of course, it doesn't make sense to keep a history for the + history principal, anyway. */ + + hist_kvno = 2; + + if (ret = kadm5_create_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES), + "to-be-random")) + goto done; + + /* this won't let us randomize the hist_princ. So we cheat. */ + + hist_princ = NULL; + + ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL); + + hist_princ = ent.principal; + + if (ret) + goto done; + + /* now read the newly-created kdb record out of the + database. */ + + if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) + goto done; + + } + + if (ret = krb5_dbe_find_enctype(handle->context, + &hist_db, + handle->params.enctype, + -1, + -1, + &key_data)) + goto done; + + if (ret = krb5_dbekd_decrypt_key_data(handle->context, &master_encblock, + key_data, &hist_key, NULL)) + goto done; + + krb5_use_enctype(handle->context, &hist_encblock, hist_key.enctype); + + if ((ret = krb5_process_key(handle->context, &hist_encblock, + &hist_key)) != KSUCCESS) + goto done; + + hist_kvno = key_data->key_data_kvno; + +done: + free(hist_name); + if (r == NULL) + free(realm); + return ret; +} + +/* + * Function: kdb_get_entry + * + * Purpose: Gets an entry from the kerberos database and breaks + * it out into a krb5_db_entry and an osa_princ_ent_t. + * + * Arguments: + * + * handle (r) the server_handle + * principal (r) the principal to get + * kdb (w) krb5_db_entry to fill in + * adb (w) osa_princ_ent_rec to fill in + * + * when the caller is done with kdb and adb, kdb_free_entry must be + * called to release them. The adb record is filled in with the + * contents of the KRB5_TL_KADM_DATA record; if that record doesn't + * exist, an empty but valid adb record is returned. + */ +krb5_error_code +kdb_get_entry(kadm5_server_handle_t handle, + krb5_principal principal, krb5_db_entry *kdb, + osa_princ_ent_rec *adb) +{ + krb5_error_code ret; + int nprincs; + krb5_boolean more; + krb5_tl_data tl_data; + XDR xdrs; + + if (ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs, + &more)) + return(ret); + + if (more) { + krb5_db_free_principal(handle->context, kdb, nprincs); + return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); + } else if (nprincs != 1) { + krb5_db_free_principal(handle->context, kdb, nprincs); + return(KADM5_UNK_PRINC); + } + + if (adb) { + memset(adb, 0, sizeof(*adb)); + + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + /* + * XXX Currently, lookup_tl_data always returns zero; it sets + * tl_data->tl_data_length to zero if the type isn't found. + * This should be fixed... + */ + if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data)) + || (tl_data.tl_data_length == 0)) { + /* there's no admin data. this can happen, if the admin + server is put into production after some principals + are created. In this case, return valid admin + data (which is all zeros with the hist_kvno filled + in), and when the entry is written, the admin + data will get stored correctly. */ + + adb->admin_history_kvno = hist_kvno; + + return(ret); + } + + xdrmem_create(&xdrs, tl_data.tl_data_contents, + tl_data.tl_data_length, XDR_DECODE); + if (! xdr_osa_princ_ent_rec(&xdrs, adb)) { + xdr_destroy(&xdrs); + krb5_db_free_principal(handle->context, kdb, 1); + return(OSA_ADB_XDR_FAILURE); + } + xdr_destroy(&xdrs); + } + + return(0); +} + +/* + * Function: kdb_free_entry + * + * Purpose: frees the resources allocated by kdb_get_entry + * + * Arguments: + * + * handle (r) the server_handle + * kdb (w) krb5_db_entry to fill in + * adb (w) osa_princ_ent_rec to fill in + * + * when the caller is done with kdb and adb, kdb_free_entry must be + * called to release them. + */ + +krb5_error_code +kdb_free_entry(kadm5_server_handle_t handle, + krb5_db_entry *kdb, osa_princ_ent_rec *adb) +{ + XDR xdrs; + + + if (kdb) + krb5_db_free_principal(handle->context, kdb, 1); + + if (adb) { + xdrmem_create(&xdrs, NULL, 0, XDR_FREE); + xdr_osa_princ_ent_rec(&xdrs, adb); + xdr_destroy(&xdrs); + } + + return(0); +} + +/* + * Function: kdb_put_entry + * + * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to + * database. + * + * Arguments: + * + * handle (r) the server_handle + * kdb (r/w) the krb5_db_entry to store + * adb (r) the osa_princ_db_ent to store + * + * Effects: + * + * The last modifier field of the kdb is set to the caller at now. + * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as + * KRB5_TL_KADM_DATA. kdb is then written to the database. + */ +krb5_error_code +kdb_put_entry(kadm5_server_handle_t handle, + krb5_db_entry *kdb, osa_princ_ent_rec *adb) +{ + krb5_error_code ret; + krb5_int32 now; + XDR xdrs; + krb5_tl_data tl_data; + int one; + + if (ret = krb5_timeofday(handle->context, &now)) + return(ret); + + if (ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now, + handle->current_caller)) + return(ret); + + xdralloc_create(&xdrs, XDR_ENCODE); + if(! xdr_osa_princ_ent_rec(&xdrs, adb)) { + xdr_destroy(&xdrs); + return(OSA_ADB_XDR_FAILURE); + } + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + tl_data.tl_data_length = xdr_getpos(&xdrs); + tl_data.tl_data_contents = xdralloc_getdata(&xdrs); + + ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data); + + xdr_destroy(&xdrs); + + if (ret) + return(ret); + + one = 1; + + if (ret = krb5_db_put_principal(handle->context, kdb, &one)) + return(ret); + + return(0); +} + +krb5_error_code +kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name) +{ + int one = 1; + krb5_error_code ret; + + ret = krb5_db_delete_principal(handle->context, name, &one); + + return ret; +} + +typedef struct _iter_data { + void (*func)(void *, krb5_principal); + void *data; +} iter_data; + +static krb5_error_code +kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb) +{ + iter_data *id = (iter_data *) data; + + (*(id->func))(id->data, kdb->princ); + + return(0); +} + +krb5_error_code +kdb_iter_entry(kadm5_server_handle_t handle, + void (*iter_fct)(void *, krb5_principal), void *data) +{ + iter_data id; + krb5_error_code ret; + + id.func = iter_fct; + id.data = data; + + if (ret = krb5_db_iterate(handle->context, kdb_iter_func, &id)) + return(ret); + + return(0); +} + + diff --git a/src/lib/kadm5/server_misc.c b/src/lib/kadm5/server_misc.c new file mode 100644 index 000000000..24f101ce5 --- /dev/null +++ b/src/lib/kadm5/server_misc.c @@ -0,0 +1,101 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "k5-int.h" +#include +#include +#include "adb.h" + +/* for strcasecmp */ +#include + +#include "server_internal.h" + +kadm5_ret_t +adb_policy_init(kadm5_server_handle_t handle) +{ + osa_adb_ret_t ret; + if(handle->policy_db == (osa_adb_policy_t) NULL) + if((ret = osa_adb_open_policy(&handle->policy_db, + &handle->params)) != OSA_ADB_OK) + return ret; + return KADM5_OK; +} + +kadm5_ret_t +adb_policy_close(kadm5_server_handle_t handle) +{ + osa_adb_ret_t ret; + if(handle->policy_db != (osa_adb_policy_t) NULL) + if((ret = osa_adb_close_policy(handle->policy_db)) != OSA_ADB_OK) + return ret; + handle->policy_db = NULL; + return KADM5_OK; +} + +/* some of this is stolen from gatekeeper ... */ +kadm5_ret_t +passwd_check(kadm5_server_handle_t handle, + char *password, int use_policy, kadm5_policy_ent_t pol, + krb5_principal principal) +{ + int nupper = 0, + nlower = 0, + ndigit = 0, + npunct = 0, + nspec = 0; + char c, *s; + + if(use_policy) { + if(strlen(password) < pol->pw_min_length) + return KADM5_PASS_Q_TOOSHORT; + s = password; + while ((c = *s++)) { + if (islower(c)) { + nlower = 1; + continue; + } + else if (isupper(c)) { + nupper = 1; + continue; + } else if (isdigit(c)) { + ndigit = 1; + continue; + } else if (ispunct(c)) { + npunct = 1; + continue; + } else { + nspec = 1; + continue; + } + } + if ((nupper + nlower + ndigit + npunct + nspec) < pol->pw_min_classes) + return KADM5_PASS_Q_CLASS; + if((find_word(password) == KADM5_OK)) + return KADM5_PASS_Q_DICT; + else { + char *cp; + int c, n = krb5_princ_size(handle->context, principal); + cp = krb5_princ_realm(handle->context, principal)->data; + if (strcasecmp(cp, password) == 0) + return KADM5_PASS_Q_DICT; + for (c = 0; c < n ; c++) { + cp = krb5_princ_component(handle->context, principal, c)->data; + if (strcasecmp(cp, password) == 0) + return KADM5_PASS_Q_DICT; + } + return KADM5_OK; + } + } else { + if (strlen(password) < 1) + return KADM5_PASS_Q_TOOSHORT; + } + return KADM5_OK; +} diff --git a/src/lib/kadm5/setenv.c b/src/lib/kadm5/setenv.c new file mode 100644 index 000000000..47904de1b --- /dev/null +++ b/src/lib/kadm5/setenv.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* based on @(#)setenv.c 5.2 (Berkeley) 6/27/88 */ + +#include +#include +#include +#include + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register char *name, *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *C; + int l_value, offset; +#if !defined(sun) && !defined(hpux) + char *malloc(), *realloc(); +#endif + char *_findenv(); + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((C = _findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return(0); + if (strlen(C) >= l_value) { /* old larger; copy over */ + while (*C++ = *value++); + return(0); + } + } + else { /* create new slot */ + register int cnt; + register char **P; + + for (P = environ, cnt = 0; *P; ++P, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (u_int)(sizeof(char *) * (cnt + 2))); + if (!environ) + return(-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + P = (char **)malloc((u_int)(sizeof(char *) * + (cnt + 2))); + if (!P) + return(-1); + memcpy(P, environ, cnt * sizeof(char *)); + environ = P; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (C = name; *C && *C != '='; ++C); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((u_int)((int)(C - name) + l_value + 2)))) + return(-1); + for (C = environ[offset]; (*C = *name++) && *C != '='; ++C); + for (*C++ = '='; *C++ = *value++;); + return(0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + char *name; +{ + extern char **environ; + register char **P; + int offset; + char *_findenv(); + + while (_findenv(name, &offset)) /* if set multiple times */ + for (P = &environ[offset];; ++P) + if (!(*P = *(P + 1))) + break; +} +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* based on @(#)getenv.c 5.5 (Berkeley) 6/27/88 */ + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + char *_findenv(); + + return(_findenv(name, &offset)); +} + +/* + * _findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + * + * This routine *should* be a static; don't use it. + */ +char * +_findenv(name, offset) + register char *name; + int *offset; +{ + extern char **environ; + register int len; + register char **P, *C; + + for (C = name, len = 0; *C && *C != '='; ++C, ++len); + for (P = environ; *P; ++P) + if (!strncmp(*P, name, len)) + if (*(C = *P + len) == '=') { + *offset = P - environ; + return(++C); + } + return(NULL); +} diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c new file mode 100644 index 000000000..882892933 --- /dev/null +++ b/src/lib/kadm5/str_conv.c @@ -0,0 +1,397 @@ +/* + * lib/kadm/str_conv.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. + * + */ + +/* + * str_conv.c - Convert between strings and Kerberos internal data. + */ + +/* + * Table of contents: + * + * String decoding: + * ---------------- + * krb5_string_to_flags() - Convert string to krb5_flags. + * + * String encoding: + * ---------------- + * krb5_flags_to_string() - Convert krb5_flags to string. + */ + +#include "k5-int.h" +#include "admin_internal.h" + +/* + * Local data structures. + */ +struct flags_lookup_entry { + krb5_flags fl_flags; /* Flag */ + krb5_boolean fl_sense; /* Sense of the flag */ + const char * fl_specifier; /* How to recognize it */ + const char * fl_output; /* How to spit it out */ +}; + +/* + * Local strings + */ + +static const char default_tupleseps[] = ", \t"; +static const char default_ksaltseps[] = ":."; + +/* Keytype strings */ +/* Flags strings */ +static const char flags_pdate_in[] = "postdateable"; +static const char flags_fwd_in[] = "forwardable"; +static const char flags_tgtbased_in[] = "tgt-based"; +static const char flags_renew_in[] = "renewable"; +static const char flags_proxy_in[] = "proxiable"; +static const char flags_dup_skey_in[] = "dup-skey"; +static const char flags_tickets_in[] = "allow-tickets"; +static const char flags_preauth_in[] = "preauth"; +static const char flags_hwauth_in[] = "hwauth"; +static const char flags_pwchange_in[] = "pwchange"; +static const char flags_service_in[] = "service"; +static const char flags_pwsvc_in[] = "pwservice"; +static const char flags_md5_in[] = "md5"; +static const char flags_pdate_out[] = "Not Postdateable"; +static const char flags_fwd_out[] = "Not Forwardable"; +static const char flags_tgtbased_out[] = "No TGT-based requests"; +static const char flags_renew_out[] = "Not renewable"; +static const char flags_proxy_out[] = "Not proxiable"; +static const char flags_dup_skey_out[] = "No DUP_SKEY requests"; +static const char flags_tickets_out[] = "All Tickets Disallowed"; +static const char flags_preauth_out[] = "Preauthorization required"; +static const char flags_hwauth_out[] = "HW Authorization required"; +static const char flags_pwchange_out[] = "Password Change required"; +static const char flags_service_out[] = "Service Disabled"; +static const char flags_pwsvc_out[] = "Password Changing Service"; +static const char flags_md5_out[] = "RSA-MD5 supported"; +static const char flags_default_neg[] = "-"; +static const char flags_default_sep[] = " "; + +/* + * Lookup tables. + */ + +static const struct flags_lookup_entry flags_table[] = { +/* flag sense input specifier output string */ +/*----------------------------- ------- ------------------ ------------------*/ +{ KRB5_KDB_DISALLOW_POSTDATED, 0, flags_pdate_in, flags_pdate_out }, +{ KRB5_KDB_DISALLOW_FORWARDABLE,0, flags_fwd_in, flags_fwd_out }, +{ KRB5_KDB_DISALLOW_TGT_BASED, 0, flags_tgtbased_in, flags_tgtbased_out}, +{ KRB5_KDB_DISALLOW_RENEWABLE, 0, flags_renew_in, flags_renew_out }, +{ KRB5_KDB_DISALLOW_PROXIABLE, 0, flags_proxy_in, flags_proxy_out }, +{ KRB5_KDB_DISALLOW_DUP_SKEY, 0, flags_dup_skey_in, flags_dup_skey_out}, +{ KRB5_KDB_DISALLOW_ALL_TIX, 0, flags_tickets_in, flags_tickets_out }, +{ KRB5_KDB_REQUIRES_PRE_AUTH, 1, flags_preauth_in, flags_preauth_out }, +{ KRB5_KDB_REQUIRES_HW_AUTH, 1, flags_hwauth_in, flags_hwauth_out }, +{ KRB5_KDB_REQUIRES_PWCHANGE, 1, flags_pwchange_in, flags_pwchange_out}, +{ KRB5_KDB_DISALLOW_SVR, 0, flags_service_in, flags_service_out }, +{ KRB5_KDB_PWCHANGE_SERVICE, 1, flags_pwsvc_in, flags_pwsvc_out }, +{ KRB5_KDB_SUPPORT_DESMD5, 1, flags_md5_in, flags_md5_out } +}; +static const int flags_table_nents = sizeof(flags_table)/ + sizeof(flags_table[0]); + + +krb5_error_code +krb5_string_to_flags(string, positive, negative, flagsp) + char * string; + const char * positive; + const char * negative; + krb5_flags * flagsp; +{ + int i; + int found; + const char *neg; + size_t nsize, psize; + int cpos; + int sense; + + found = 0; + /* We need to have a way to negate it. */ + neg = (negative) ? negative : flags_default_neg; + nsize = strlen(neg); + psize = (positive) ? strlen(positive) : 0; + + cpos = 0; + sense = 1; + /* First check for positive or negative sense */ + if (!strncasecmp(neg, string, nsize)) { + sense = 0; + cpos += (int) nsize; + } + else if (psize && !strncasecmp(positive, string, psize)) { + cpos += (int) psize; + } + + for (i=0; i= flags_table_nents) return ENOENT; /* End of list */ + if(strlen(flags_table[flag].fl_specifier) > buflen) return ENOMEM; + strcpy(buffer, flags_table[flag].fl_specifier); + return 0; +} + +/* + * krb5_keysalt_is_present() - Determine if a key/salt pair is present + * in a list of key/salt tuples. + * + * Salttype may be negative to indicate a search for only a enctype. + */ +krb5_boolean +krb5_keysalt_is_present(ksaltlist, nksalts, enctype, salttype) + krb5_key_salt_tuple *ksaltlist; + krb5_int32 nksalts; + krb5_enctype enctype; + krb5_int32 salttype; +{ + krb5_boolean foundit; + int i; + + foundit = 0; + if (ksaltlist) { + for (i=0; i + * or + * + */ + sp = (char *) NULL; + /* Attempt to find a separator */ + septmp = ksseplist; + for (sp = strchr(kp, (int) *septmp); + *(++septmp) && !sp; + ep = strchr(kp, (int) *septmp)); + + if (sp) { + /* Separate enctype from salttype */ + sepchar = *sp; + *sp = '\0'; + sp++; + } + else + stype = -1; + + /* + * Attempt to parse enctype and salttype. If we parse well + * then make sure that it specifies a unique key/salt combo + */ + if (!(kret = krb5_string_to_enctype(kp, &ktype)) && + (!sp || !(kret = krb5_string_to_salttype(sp, &stype))) && + (dups || + !krb5_keysalt_is_present(*ksaltp, *nksaltp, ktype, stype))) { + + /* Squirrel away old keysalt array */ + savep = *ksaltp; + len = (size_t) *nksaltp; + + /* Get new keysalt array */ + if (*ksaltp = (krb5_key_salt_tuple *) + malloc((len + 1) * sizeof(krb5_key_salt_tuple))) { + + /* Copy old keysalt if appropriate */ + if (savep) { + memcpy(*ksaltp, savep, + len * sizeof(krb5_key_salt_tuple)); + krb5_xfree(savep); + } + + /* Save our values */ + (*ksaltp)[(*nksaltp)].ks_enctype = ktype; + (*ksaltp)[(*nksaltp)].ks_salttype = stype; + (*nksaltp)++; + } + else { + *ksaltp = savep; + break; + } + } + if (kret) + return kret; + if (sp) + sp[-1] = sepchar; + if (ep) + ep[-1] = trailchar; + kp = ep; + } + return(kret); +} + +/* + * krb5_keysalt_iterate() - Do something for each unique key/salt + * combination. + * + * If ignoresalt set, then salttype is ignored. + */ +krb5_error_code +krb5_keysalt_iterate(ksaltlist, nksalt, ignoresalt, iterator, arg) + krb5_key_salt_tuple *ksaltlist; + krb5_int32 nksalt; + krb5_boolean ignoresalt; + krb5_error_code (*iterator) KRB5_NPROTOTYPE((krb5_key_salt_tuple *, + krb5_pointer)); + krb5_pointer arg; +{ + int i; + krb5_error_code kret; + krb5_key_salt_tuple scratch; + + kret = 0; + for (i=0; i +#include "server_internal.h" + +kadm5_ret_t kadm5_chpass_principal_util(void *server_handle, + krb5_principal princ, + char *new_pw, + char **ret_pw, + char *msg_ret) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + return _kadm5_chpass_principal_util(handle, handle->lhandle, princ, + new_pw, ret_pw, msg_ret); +} diff --git a/src/lib/kadm5/svr_iters.c b/src/lib/kadm5/svr_iters.c new file mode 100644 index 000000000..19c900021 --- /dev/null +++ b/src/lib/kadm5/svr_iters.c @@ -0,0 +1,248 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#if defined(HAVE_COMPILE) && defined(HAVE_STEP) +#define SOLARIS_REGEXPS +#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC) +#define POSIX_REGEXPS +#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC) +#define BSD_REGEXPS +#else +#error I cannot find any regexp functions +#endif + +#include +#include +#include +#include "adb.h" +#include +#ifdef SOLARIS_REGEXPS +#include +#endif +#ifdef POSIX_REGEXPS +#include +#endif +#include + +#include "server_internal.h" + +struct iter_data { + krb5_context context; + DynObject matches; + char *exp; +#ifdef SOLARIS_REGEXPS + char *expbuf; +#endif +#ifdef POSIX_REGEXPS + regex_t preg; +#endif +}; + +/* + * Function: glob_to_regexp + * + * Arguments: + * + * glob (r) the shell-style glob (?*[]) to convert + * realm (r) the default realm to append, or NULL + * regexp (w) the ed-style regexp created from glob + * + * Effects: + * + * regexp is filled in with allocated memory contained a regular + * expression to be used with re_comp/compile that matches what the + * shell-style glob would match. If glob does not contain an "@" + * character and realm is not NULL, "@" is appended to the regexp. + * + * Conversion algorithm: + * + * quoted characters are copied quoted + * ? is converted to . + * * is converted to .* + * active characters are quoted: ^, $, . + * [ and ] are active but supported and have the same meaning, so + * they are copied + * other characters are copied + * regexp is anchored with ^ and $ + */ +kadm5_ret_t glob_to_regexp(char *glob, char *realm, char **regexp) +{ + int append_realm; + char *p; + + /* validate the glob */ + if (glob[strlen(glob)-1] == '\\') + return EINVAL; + + /* A character of glob can turn into two in regexp, plus ^ and $ */ + /* and trailing null. If glob has no @, also allocate space for */ + /* the realm. */ + append_realm = (realm != NULL) && (strchr(glob, '@') == NULL); + p = (char *) malloc(strlen(glob)*2+ 3 + + (append_realm ? (strlen(realm)+1) : 0)); + if (p == NULL) + return ENOMEM; + *regexp = p; + + *p++ = '^'; + while (*glob) { + switch (*glob) { + case '?': + *p++ = '.'; + break; + case '*': + *p++ = '.'; + *p++ = '*'; + break; + case '.': + case '^': + case '$': + *p++ = '\\'; + *p++ = *glob; + break; + case '\\': + *p++ = '\\'; + *p++ = ++*glob; + break; + default: + *p++ = *glob; + break; + } + glob++; + } + + if (append_realm) { + *p++ = '@'; + strcpy(p, realm); + p += strlen(realm); + } + + *p++ = '$'; + *p++ = '\0'; + return KADM5_OK; +} + +void get_either_iter(struct iter_data *data, char *name) +{ + if ( +#ifdef SOLARIS_REGEXPS + (step(name, data->expbuf) != 0) +#endif +#ifdef POSIX_REGEXPS + (regexec(&data->preg, name, 0, NULL, 0) == 0) +#endif +#ifdef BSD_REGEXPS + (re_exec(name) != 0) +#endif + ) + { + (void) DynAdd(data->matches, &name); + } else + free(name); +} + +void get_pols_iter(void *data, osa_policy_ent_t entry) +{ + char *name; + + if ((name = strdup(entry->name)) == NULL) + return; + get_either_iter(data, name); +} + +void get_princs_iter(void *data, krb5_principal princ) +{ + struct iter_data *id = (struct iter_data *) data; + char *name; + + if (krb5_unparse_name(id->context, princ, &name) != 0) + return; + get_either_iter(data, name); +} + +kadm5_ret_t kadm5_get_either(int princ, + void *server_handle, + char *exp, + char ***princs, + int *count) +{ + struct iter_data data; + char *msg, *regexp; + int ret; + kadm5_server_handle_t handle = server_handle; + + *count = 0; + if (exp == NULL) + exp = "*"; + + CHECK_HANDLE(server_handle); + + if ((ret = glob_to_regexp(exp, princ ? handle->params.realm : NULL, + ®exp)) != KADM5_OK) + return ret; + + if ( +#ifdef SOLARIS_REGEXPS + ((data.expbuf = compile(regexp, NULL, NULL)) == NULL) +#endif +#ifdef POSIX_REGEXPS + ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0) +#endif +#ifdef BSD_REGEXPS + ((msg = (char *) re_comp(regexp)) != NULL) +#endif + ) + { + /* XXX syslog msg or regerr(regerrno) */ + free(regexp); + return EINVAL; + } + + if ((data.matches = DynCreate(sizeof(char *), -4)) == NULL) { + free(regexp); + return ENOMEM; + } + + if (princ) { + data.context = handle->context; + ret = kdb_iter_entry(handle, get_princs_iter, (void *) &data); + } else { + ret = osa_adb_iter_policy(handle->policy_db, get_pols_iter, (void *)&data); + } + + if (ret != OSA_ADB_OK) { + free(regexp); + DynDestroy(data.matches); + return ret; + } + + (*princs) = (char **) DynArray(data.matches); + *count = DynSize(data.matches); + DynRelease(data.matches); + free(regexp); + return KADM5_OK; +} + +kadm5_ret_t kadm5_get_principals(void *server_handle, + char *exp, + char ***princs, + int *count) +{ + return kadm5_get_either(1, server_handle, exp, princs, count); +} + +kadm5_ret_t kadm5_get_policies(void *server_handle, + char *exp, + char ***pols, + int *count) +{ + return kadm5_get_either(0, server_handle, exp, pols, count); +} + diff --git a/src/lib/kadm5/svr_misc_free.c b/src/lib/kadm5/svr_misc_free.c new file mode 100644 index 000000000..5c76a1ebc --- /dev/null +++ b/src/lib/kadm5/svr_misc_free.c @@ -0,0 +1,37 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif +#include +#include +#include "server_internal.h" + +kadm5_ret_t +kadm5_free_principal_ent(void *server_handle, + kadm5_principal_ent_t val) +{ + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if(val) { + if(val->principal) + krb5_free_principal(handle->context, val->principal); + if(val->mod_name) + krb5_free_principal(handle->context, val->mod_name); + if(val->policy) + free(val->policy); + + /* XXX free key_data and tl_data */ + + if (handle->api_version == KADM5_API_VERSION_1) + free(val); + } + return KADM5_OK; +} diff --git a/src/lib/kadm5/svr_policy.c b/src/lib/kadm5/svr_policy.c new file mode 100644 index 000000000..74e252115 --- /dev/null +++ b/src/lib/kadm5/svr_policy.c @@ -0,0 +1,315 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include "adb.h" +#include "server_internal.h" +#include + +#define MAX_PW_HISTORY 10 +#define MIN_PW_HISTORY 1 +#define MIN_PW_CLASSES 1 +#define MAX_PW_CLASSES 5 +#define MIN_PW_LENGTH 1 + +/* + * Function: kadm5_create_policy + * + * Purpose: Create Policies in the policy DB. + * + * Arguments: + * entry (input) The policy entry to be written out to the DB. + * mask (input) Specifies which fields in entry are to ge written out + * and which get default values. + * 0 if sucsessfull otherwise an error code is returned. + * + * Requires: + * Entry must be a valid principal entry, and mask have a valid value. + * + * Effects: + * Verifies that mask does not specify that the refcount should + * be set as part of the creation, and calls + * kadm5_create_policy_internal. If the refcount *is* + * specified, returns KADM5_BAD_MASK. + */ + +kadm5_ret_t +kadm5_create_policy(void *server_handle, + kadm5_policy_ent_t entry, long mask) +{ + CHECK_HANDLE(server_handle); + + if (mask & KADM5_REF_COUNT) + return KADM5_BAD_MASK; + else + return kadm5_create_policy_internal(server_handle, entry, mask); +} + +/* + * Function: kadm5_create_policy_internal + * + * Purpose: Create Policies in the policy DB. + * + * Arguments: + * entry (input) The policy entry to be written out to the DB. + * mask (input) Specifies which fields in entry are to ge written out + * and which get default values. + * 0 if sucsessfull otherwise an error code is returned. + * + * Requires: + * Entry must be a valid principal entry, and mask have a valid value. + * + * Effects: + * Writes the data to the database, and does a database sync if + * sucsessfull. + * + */ + +kadm5_ret_t +kadm5_create_policy_internal(void *server_handle, + kadm5_policy_ent_t entry, long mask) +{ + kadm5_server_handle_t handle = server_handle; + osa_policy_ent_rec pent; + int ret; + char *p; + + CHECK_HANDLE(server_handle); + + if ((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL)) + return EINVAL; + if(strlen(entry->policy) == 0) + return KADM5_BAD_POLICY; + if (!(mask & KADM5_POLICY)) + return KADM5_BAD_MASK; + + pent.name = entry->policy; + p = entry->policy; + while(*p != '\0') { + if(*p < ' ' || *p > '~') + return KADM5_BAD_POLICY; + else + p++; + } + if (!(mask & KADM5_PW_MAX_LIFE)) + pent.pw_max_life = 0; + else + pent.pw_max_life = entry->pw_max_life; + if (!(mask & KADM5_PW_MIN_LIFE)) + pent.pw_min_life = 0; + else { + if((mask & KADM5_PW_MAX_LIFE)) { + if(entry->pw_min_life > entry->pw_max_life && entry->pw_max_life != 0) + return KADM5_BAD_MIN_PASS_LIFE; + } + pent.pw_min_life = entry->pw_min_life; + } + if (!(mask & KADM5_PW_MIN_LENGTH)) + pent.pw_min_length = MIN_PW_LENGTH; + else { + if(entry->pw_min_length < MIN_PW_LENGTH) + return KADM5_BAD_LENGTH; + pent.pw_min_length = entry->pw_min_length; + } + if (!(mask & KADM5_PW_MIN_CLASSES)) + pent.pw_min_classes = MIN_PW_CLASSES; + else { + if(entry->pw_min_classes > MAX_PW_CLASSES || entry->pw_min_classes < MIN_PW_CLASSES) + return KADM5_BAD_CLASS; + pent.pw_min_classes = entry->pw_min_classes; + } + if (!(mask & KADM5_PW_HISTORY_NUM)) + pent.pw_history_num = MIN_PW_HISTORY; + else { + if(entry->pw_history_num < MIN_PW_HISTORY || + entry->pw_history_num > MAX_PW_HISTORY) + return KADM5_BAD_HISTORY; + else + pent.pw_history_num = entry->pw_history_num; + } + if (!(mask & KADM5_REF_COUNT)) + pent.policy_refcnt = 0; + else + pent.policy_refcnt = entry->policy_refcnt; + if ((ret = osa_adb_create_policy(handle->policy_db, &pent)) == OSA_ADB_OK) + return KADM5_OK; + else + return ret; +} + +kadm5_ret_t +kadm5_delete_policy(void *server_handle, kadm5_policy_t name) +{ + kadm5_server_handle_t handle = server_handle; + osa_policy_ent_t entry; + int ret; + + CHECK_HANDLE(server_handle); + + if(name == (kadm5_policy_t) NULL) + return EINVAL; + if(strlen(name) == 0) + return KADM5_BAD_POLICY; + if ((ret = osa_adb_get_policy(handle->policy_db, name, &entry)) != OSA_ADB_OK) + return ret; + if(entry->policy_refcnt != 0) { + osa_free_policy_ent(entry); + return KADM5_POLICY_REF; + } + osa_free_policy_ent(entry); + if ((ret = osa_adb_destroy_policy(handle->policy_db, name)) == OSA_ADB_OK) + return KADM5_OK; + else + return ret; +} + +kadm5_ret_t +kadm5_modify_policy(void *server_handle, + kadm5_policy_ent_t entry, long mask) +{ + CHECK_HANDLE(server_handle); + + if (mask & KADM5_REF_COUNT) + return KADM5_BAD_MASK; + else + return kadm5_modify_policy_internal(server_handle, entry, mask); +} + +kadm5_ret_t +kadm5_modify_policy_internal(void *server_handle, + kadm5_policy_ent_t entry, long mask) +{ + kadm5_server_handle_t handle = server_handle; + osa_policy_ent_t p; + int ret; + + CHECK_HANDLE(server_handle); + + if((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL)) + return EINVAL; + if(strlen(entry->policy) == 0) + return KADM5_BAD_POLICY; + if((mask & KADM5_POLICY)) + return KADM5_BAD_MASK; + + switch ((ret = osa_adb_get_policy(handle->policy_db, entry->policy, &p))) { + case OSA_ADB_OK: + break; + case OSA_ADB_NOENT: + return KADM5_UNK_POLICY; + default: + break; + } + if ((mask & KADM5_PW_MAX_LIFE)) + p->pw_max_life = entry->pw_max_life; + if ((mask & KADM5_PW_MIN_LIFE)) { + if(entry->pw_min_life > p->pw_max_life && p->pw_max_life != 0) { + osa_free_policy_ent(p); + return KADM5_BAD_MIN_PASS_LIFE; + } + p->pw_min_life = entry->pw_min_life; + } + if ((mask & KADM5_PW_MIN_LENGTH)) { + if(entry->pw_min_length < MIN_PW_LENGTH) { + osa_free_policy_ent(p); + return KADM5_BAD_LENGTH; + } + p->pw_min_length = entry->pw_min_length; + } + if ((mask & KADM5_PW_MIN_CLASSES)) { + if(entry->pw_min_classes > MAX_PW_CLASSES || + entry->pw_min_classes < MIN_PW_CLASSES) { + osa_free_policy_ent(p); + return KADM5_BAD_CLASS; + } + p->pw_min_classes = entry->pw_min_classes; + } + if ((mask & KADM5_PW_HISTORY_NUM)) { + if(entry->pw_history_num < MIN_PW_HISTORY || + entry->pw_history_num > MAX_PW_HISTORY) { + osa_free_policy_ent(p); + return KADM5_BAD_HISTORY; + } + p->pw_history_num = entry->pw_history_num; + } + if ((mask & KADM5_REF_COUNT)) + p->policy_refcnt = entry->policy_refcnt; + switch ((ret = osa_adb_put_policy(handle->policy_db, p))) { + case OSA_ADB_OK: + ret = KADM5_OK; + break; + case OSA_ADB_NOENT: /* this should not happen here ... */ + ret = KADM5_UNK_POLICY; + break; + } + osa_free_policy_ent(p); + return ret; +} + +kadm5_ret_t +kadm5_get_policy(void *server_handle, kadm5_policy_t name, + kadm5_policy_ent_t entry) +{ + osa_policy_ent_t t; + kadm5_policy_ent_rec entry_local, **entry_orig, *new; + int ret; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + /* + * In version 1, entry is a pointer to a kadm5_policy_ent_t that + * should be filled with allocated memory. + */ + if (handle->api_version == KADM5_API_VERSION_1) { + entry_orig = (kadm5_policy_ent_rec **) entry; + *entry_orig = NULL; + entry = &entry_local; + } + + if (name == (kadm5_policy_t) NULL) + return EINVAL; + if(strlen(name) == 0) + return KADM5_BAD_POLICY; + switch((ret = osa_adb_get_policy(handle->policy_db, name, &t))) { + case OSA_ADB_OK: + break; + case OSA_ADB_NOENT: + return KADM5_UNK_POLICY; + default: + return ret; + } + if ((entry->policy = (char *) malloc(strlen(t->name) + 1)) == NULL) { + osa_free_policy_ent(t); + return ENOMEM; + } + strcpy(entry->policy, t->name); + entry->pw_min_life = t->pw_min_life; + entry->pw_max_life = t->pw_max_life; + entry->pw_min_length = t->pw_min_length; + entry->pw_min_classes = t->pw_min_classes; + entry->pw_history_num = t->pw_history_num; + entry->policy_refcnt = t->policy_refcnt; + osa_free_policy_ent(t); + + if (handle->api_version == KADM5_API_VERSION_1) { + new = (kadm5_policy_ent_t) malloc(sizeof(kadm5_policy_ent_rec)); + if (new == NULL) { + free(entry->policy); + osa_free_policy_ent(t); + return ENOMEM; + } + *new = *entry; + *entry_orig = new; + } + + return KADM5_OK; +} diff --git a/src/lib/kadm5/svr_principal.c b/src/lib/kadm5/svr_principal.c new file mode 100644 index 000000000..6f9671fcf --- /dev/null +++ b/src/lib/kadm5/svr_principal.c @@ -0,0 +1,1350 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include "adb.h" +#include "k5-int.h" +#include +#include +#include +#include "server_internal.h" +#include +#include + +extern krb5_principal master_princ; +extern krb5_principal hist_princ; +extern krb5_encrypt_block master_encblock; +extern krb5_encrypt_block hist_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_keyblock hist_key; +extern krb5_db_entry master_db; +extern krb5_db_entry hist_db; +extern krb5_kvno hist_kvno; + +static int decrypt_key_data(krb5_context context, + int n_key_data, krb5_key_data *key_data, + krb5_keyblock **keyblocks, int *n_keys); + +/* + * XXX Functions that ought to be in libkrb5.a, but aren't. + */ +int krb5_free_keyblock_contents(context, key) + krb5_context context; + krb5_keyblock *key; +{ + memset(key->contents, 0, key->length); + krb5_xfree(key->contents); + return 0; +} + +kadm5_ret_t krb5_copy_key_data_contents(context, from, to) + krb5_context context; + krb5_key_data *from, *to; +{ + int i, idx; + + *to = *from; + + idx = (from->key_data_ver == 1 ? 1 : 2); + + for (i = 0; i < idx; i++) { + to->key_data_contents[i] = malloc(from->key_data_length[i]); + if (to->key_data_contents[i] == NULL) { + for (i = 0; i < idx; i++) { + if (to->key_data_contents[i]) { + memset(to->key_data_contents[i], 0, + to->key_data_length[i]); + free(to->key_data_contents[i]); + } + } + return ENOMEM; + } + memcpy(to->key_data_contents[i], from->key_data_contents[i], + from->key_data_length[i]); + } + return 0; +} + +static krb5_tl_data *dup_tl_data(krb5_tl_data *tl) +{ + krb5_tl_data *n; + + n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); + if (n == NULL) + return NULL; + n->tl_data_contents = malloc(tl->tl_data_length); + if (n->tl_data_contents == NULL) { + free(n); + return NULL; + } + memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length); + n->tl_data_type = tl->tl_data_type; + n->tl_data_length = tl->tl_data_length; + n->tl_data_next = NULL; + return n; +} + +kadm5_ret_t +kadm5_create_principal(void *server_handle, + kadm5_principal_ent_t entry, long mask, + char *password) +{ + krb5_db_entry kdb; + osa_princ_ent_rec adb; + kadm5_policy_ent_rec polent; + krb5_int32 now; + krb5_tl_data *tl_data_orig, *tl_data_tail; + unsigned int ret; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + /* + * Argument sanity checking, and opening up the DB + */ + if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) || + (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) || + (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) || + (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) || + (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) || + (mask & KADM5_FAIL_AUTH_COUNT)) + return KADM5_BAD_MASK; + if((mask & ~ALL_PRINC_MASK)) + return KADM5_BAD_MASK; + if (entry == (kadm5_principal_ent_t) NULL || password == NULL) + return EINVAL; + + /* + * Check to see if the principal exists + */ + ret = kdb_get_entry(handle, entry->principal, &kdb, &adb); + + switch(ret) { + case KADM5_UNK_PRINC: + break; + case 0: + kdb_free_entry(handle, &kdb, &adb); + return KADM5_DUP; + default: + return ret; + } + + memset(&kdb, 0, sizeof(krb5_db_entry)); + memset(&adb, 0, sizeof(osa_princ_ent_rec)); + + /* + * If a policy was specified, load it. + * If we can not find the one specified return an error + */ + if ((mask & KADM5_POLICY)) { + if ((ret = kadm5_get_policy(handle->lhandle, entry->policy, + &polent)) != KADM5_OK) { + if(ret == EINVAL) + return KADM5_BAD_POLICY; + else + return ret; + } + } + if (ret = passwd_check(handle, password, (mask & KADM5_POLICY), + &polent, entry->principal)) { + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return ret; + } + /* + * Start populating the various DB fields, using the + * "defaults" for fields that were not specified by the + * mask. + */ + if (ret = krb5_timeofday(handle->context, &now)) { + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return ret; + } + + kdb.magic = KRB5_KDB_MAGIC_NUMBER; + kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */ + + if ((mask & KADM5_ATTRIBUTES)) + kdb.attributes = entry->attributes; + else + kdb.attributes = handle->params.flags; + + if ((mask & KADM5_MAX_LIFE)) + kdb.max_life = entry->max_life; + else + kdb.max_life = handle->params.max_life; + + if (mask & KADM5_MAX_RLIFE) + kdb.max_renewable_life = entry->max_renewable_life; + else + kdb.max_renewable_life = handle->params.max_rlife; + + if ((mask & KADM5_PRINC_EXPIRE_TIME)) + kdb.expiration = entry->princ_expire_time; + else + kdb.expiration = handle->params.expiration; + + kdb.pw_expiration = 0; + if ((mask & KADM5_POLICY)) { + if(polent.pw_max_life) + kdb.pw_expiration = now + polent.pw_max_life; + else + kdb.pw_expiration = 0; + } + if ((mask & KADM5_PW_EXPIRATION)) { + if(!kdb.pw_expiration) + kdb.pw_expiration = entry->pw_expiration; + else { + if(entry->pw_expiration != 0) + kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ? + entry->pw_expiration : kdb.pw_expiration; + } + } + + kdb.last_success = 0; + kdb.last_failed = 0; + kdb.fail_auth_count = 0; + + /* this is kind of gross, but in order to free the tl data, I need + to free the entire kdb entry, and that will try to free the + principal. */ + + if (ret = krb5_copy_principal(handle->context, + entry->principal, &(kdb.princ))) { + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return(ret); + } + + if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) { + krb5_dbe_free_contents(handle->context, &kdb); + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return(ret); + } + + /* initialize the keys */ + + if (ret = krb5_dbe_cpw(handle->context, &master_encblock, + handle->params.keysalts, + handle->params.num_keysalts, + password, + (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) { + krb5_dbe_free_contents(handle->context, &kdb); + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return(ret); + } + + /* populate the admin-server-specific fields. In the OV server, + this used to be in a separate database. Since there's already + marshalling code for the admin fields, to keep things simple, + I'm going to keep it, and make all the admin stuff occupy a + single tl_data record, */ + + adb.admin_history_kvno = hist_kvno; + if ((mask & KADM5_POLICY)) { + adb.aux_attributes = KADM5_POLICY; + + /* this does *not* need to be strdup'ed, because adb is xdr */ + /* encoded in osa_adb_create_princ, and not ever freed */ + + adb.policy = entry->policy; + } + + /* increment the policy ref count, if any */ + + if ((mask & KADM5_POLICY)) { + polent.policy_refcnt++; + if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, + KADM5_REF_COUNT)) + != KADM5_OK) { + krb5_dbe_free_contents(handle->context, &kdb); + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return(ret); + } + } + + if (mask & KADM5_TL_DATA) { + /* splice entry->tl_data onto the front of kdb.tl_data */ + tl_data_orig = kdb.tl_data; + for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next; + tl_data_tail = tl_data_tail->tl_data_next) + ; + tl_data_tail->tl_data_next = kdb.tl_data; + kdb.tl_data = entry->tl_data; + } + + /* store the new db entry */ + ret = kdb_put_entry(handle, &kdb, &adb); + + if (mask & KADM5_TL_DATA) { + /* remove entry->tl_data from the front of kdb.tl_data */ + tl_data_tail->tl_data_next = NULL; + kdb.tl_data = tl_data_orig; + } + + krb5_dbe_free_contents(handle->context, &kdb); + + if (ret) { + if ((mask & KADM5_POLICY)) { + /* decrement the policy ref count */ + + polent.policy_refcnt--; + /* + * if this fails, there's nothing we can do anyway. the + * policy refcount wil be too high. + */ + (void) kadm5_modify_policy_internal(handle->lhandle, &polent, + KADM5_REF_COUNT); + } + + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + return(ret); + } + + if (mask & KADM5_POLICY) + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + + return KADM5_OK; +} + + +kadm5_ret_t +kadm5_delete_principal(void *server_handle, krb5_principal principal) +{ + unsigned int ret; + kadm5_policy_ent_rec polent; + krb5_db_entry kdb; + osa_princ_ent_rec adb; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if (principal == NULL) + return EINVAL; + + if (ret = kdb_get_entry(handle, principal, &kdb, &adb)) + return(ret); + + if ((adb.aux_attributes & KADM5_POLICY)) { + if ((ret = kadm5_get_policy(handle->lhandle, + adb.policy, &polent)) + == KADM5_OK) { + polent.policy_refcnt--; + if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent, + KADM5_REF_COUNT)) + != KADM5_OK) { + (void) kadm5_free_policy_ent(handle->lhandle, &polent); + kdb_free_entry(handle, &kdb, &adb); + return(ret); + } + } + if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) { + kdb_free_entry(handle, &kdb, &adb); + return ret; + } + } + + ret = kdb_delete_entry(handle, principal); + + kdb_free_entry(handle, &kdb, &adb); + + return ret; +} + +kadm5_ret_t +kadm5_modify_principal(void *server_handle, + kadm5_principal_ent_t entry, long mask) +{ + int ret, ret2, i; + kadm5_policy_ent_rec npol, opol; + int have_npol = 0, have_opol = 0; + krb5_db_entry kdb; + krb5_tl_data *tl_data_orig, *tl_data_tail; + osa_princ_ent_rec adb; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) || + (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) || + (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) || + (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) || + (mask & KADM5_LAST_FAILED)) + return KADM5_BAD_MASK; + if((mask & ~ALL_PRINC_MASK)) + return KADM5_BAD_MASK; + if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR)) + return KADM5_BAD_MASK; + if(entry == (kadm5_principal_ent_t) NULL) + return EINVAL; + + if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb)) + return(ret); + + /* + * This is pretty much the same as create ... + */ + + if ((mask & KADM5_POLICY)) { + ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol); + switch(ret) { + case EINVAL: + ret = KADM5_BAD_POLICY; + break; + case KADM5_UNK_POLICY: + case KADM5_BAD_POLICY: + ret = KADM5_UNK_POLICY; + goto done; + break; + case KADM5_OK: + have_npol = 1; + if(adb.aux_attributes & KADM5_POLICY) { + if(strcmp(adb.policy, entry->policy)) { + ret = kadm5_get_policy(handle->lhandle, + adb.policy, &opol); + switch(ret) { + case EINVAL: + case KADM5_BAD_POLICY: + case KADM5_UNK_POLICY: + break; + case KADM5_OK: + have_opol = 1; + opol.policy_refcnt--; + break; + default: + goto done; + break; + } + npol.policy_refcnt++; + } + } else npol.policy_refcnt++; + adb.aux_attributes |= KADM5_POLICY; + if (adb.policy) + free(adb.policy); + adb.policy = strdup(entry->policy); + if (npol.pw_max_life) { + if (ret = + krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, + &(kdb.pw_expiration))) + goto done; + kdb.pw_expiration += npol.pw_max_life; + } else { + kdb.pw_expiration = 0; + } + break; + default: + goto done; + } + if ((mask & KADM5_PW_EXPIRATION)) { + if(kdb.pw_expiration == 0) + kdb.pw_expiration = entry->pw_expiration; + else if(entry->pw_expiration != 0) + kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ? + entry->pw_expiration : kdb.pw_expiration; + } + } + if ((mask & KADM5_PW_EXPIRATION) && !(mask & KADM5_POLICY)) { + if(kdb.pw_expiration == 0) + kdb.pw_expiration = entry->pw_expiration; + else if(entry->pw_expiration != 0) + kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ? + entry->pw_expiration : kdb.pw_expiration; + } + + if ((mask & KADM5_POLICY_CLR)) { + if (adb.aux_attributes & KADM5_POLICY) { + adb.aux_attributes &= ~KADM5_POLICY; + kdb.pw_expiration = 0; + ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol); + switch(ret) { + case EINVAL: + case KADM5_BAD_POLICY: + case KADM5_UNK_POLICY: + ret = KADM5_BAD_DB; + goto done; + break; + case KADM5_OK: + have_opol = 1; + if (adb.policy) + free(adb.policy); + adb.policy = NULL; + opol.policy_refcnt--; + break; + default: + goto done; + break; + } + } + } + if (((mask & KADM5_POLICY) || + (mask & KADM5_POLICY_CLR)) && + (((have_opol) && + (ret = + kadm5_modify_policy_internal(handle->lhandle, &opol, + KADM5_REF_COUNT))) || + ((have_npol) && + (ret = + kadm5_modify_policy_internal(handle->lhandle, &npol, + KADM5_REF_COUNT))))) + goto done; + + if ((mask & KADM5_ATTRIBUTES)) + kdb.attributes = entry->attributes; + if ((mask & KADM5_MAX_LIFE)) + kdb.max_life = entry->max_life; + if ((mask & KADM5_PRINC_EXPIRE_TIME)) + kdb.expiration = entry->princ_expire_time; + /* the pw_expiration logic would go here if it wasn't spread + all over the policy code */ + if (mask & KADM5_MAX_RLIFE) + kdb.max_renewable_life = entry->max_renewable_life; + if (mask & KADM5_FAIL_AUTH_COUNT) + kdb.fail_auth_count = entry->fail_auth_count; + + if((mask & KADM5_KVNO)) { + for (i = 0; i < kdb.n_key_data; i++) + kdb.key_data[i].key_data_kvno = entry->kvno; + } + + if (mask & KADM5_TL_DATA) { + /* splice entry->tl_data onto the front of kdb.tl_data */ + tl_data_orig = kdb.tl_data; + for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next; + tl_data_tail = tl_data_tail->tl_data_next) + ; + tl_data_tail->tl_data_next = kdb.tl_data; + kdb.tl_data = entry->tl_data; + } + + if ((ret = kdb_put_entry(handle, &kdb, &adb))) + goto done; + + if (mask & KADM5_TL_DATA) { + /* remove entry->tl_data from the front of kdb.tl_data */ + tl_data_tail->tl_data_next = NULL; + kdb.tl_data = tl_data_orig; + } + + ret = KADM5_OK; +done: + if (have_opol) { + ret2 = kadm5_free_policy_ent(handle->lhandle, &opol); + ret = ret ? ret : ret2; + } + if (have_npol) { + ret2 = kadm5_free_policy_ent(handle->lhandle, &npol); + ret = ret ? ret : ret2; + } + kdb_free_entry(handle, &kdb, &adb); + return ret; +} + +kadm5_ret_t +kadm5_rename_principal(void *server_handle, + krb5_principal source, krb5_principal target) +{ + krb5_db_entry kdb; + osa_princ_ent_rec adb; + int ret, i; + kadm5_server_handle_t handle = server_handle; + + CHECK_HANDLE(server_handle); + + if (source == NULL || target == NULL) + return EINVAL; + + if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) { + kdb_free_entry(handle, &kdb, &adb); + return(KADM5_DUP); + } + + if ((ret = kdb_get_entry(handle, source, &kdb, &adb))) + return ret; + + /* this is kinda gross, but unavoidable */ + + for (i=0; icontext, kdb.princ); + if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) { + kdb.princ = NULL; /* so freeing the dbe doesn't lose */ + goto done; + } + + if ((ret = kdb_put_entry(handle, &kdb, &adb))) + goto done; + + ret = kdb_delete_entry(handle, source); + +done: + kdb_free_entry(handle, &kdb, &adb); + return ret; +} + +kadm5_ret_t +kadm5_get_principal(void *server_handle, krb5_principal principal, + kadm5_principal_ent_t entry, + long in_mask) +{ + krb5_db_entry kdb; + osa_princ_ent_rec adb; + osa_adb_ret_t ret = 0; + long mask; + int i; + kadm5_server_handle_t handle = server_handle; + kadm5_principal_ent_rec entry_local, *entry_orig; + + CHECK_HANDLE(server_handle); + + /* + * In version 1, all the defined fields are always returned. + * entry is a pointer to a kadm5_principal_ent_t_v1 that should be + * filled with allocated memory. + */ + if (handle->api_version == KADM5_API_VERSION_1) { + mask = KADM5_PRINCIPAL_NORMAL_MASK; + entry_orig = entry; + entry = &entry_local; + } else { + mask = in_mask; + } + + memset((char *) entry, 0, sizeof(*entry)); + + if (principal == NULL) + return EINVAL; + + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return ret; + + if ((mask & KADM5_POLICY) && + adb.policy && (adb.aux_attributes & KADM5_POLICY)) { + if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) { + ret = ENOMEM; + goto done; + } + strcpy(entry->policy, adb.policy); + } + + if (mask & KADM5_AUX_ATTRIBUTES) + entry->aux_attributes = adb.aux_attributes; + + if ((mask & KADM5_PRINCIPAL) && + (ret = krb5_copy_principal(handle->context, principal, + &entry->principal))) { + goto done; + } + + if (mask & KADM5_PRINC_EXPIRE_TIME) + entry->princ_expire_time = kdb.expiration; + + if ((mask & KADM5_LAST_PWD_CHANGE) && + (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb, + &(entry->last_pwd_change)))) { + goto done; + } + + if (mask & KADM5_PW_EXPIRATION) + entry->pw_expiration = kdb.pw_expiration; + if (mask & KADM5_MAX_LIFE) + entry->max_life = kdb.max_life; + + /* this is a little non-sensical because the function returns two */ + /* values that must be checked separately against the mask */ + if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) { + if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb, + &(entry->mod_date), + &(entry->mod_name))) { + goto done; + } + if (! (mask & KADM5_MOD_TIME)) + entry->mod_date = 0; + if (! (mask & KADM5_MOD_NAME)) { + krb5_free_principal(handle->context, entry->principal); + entry->principal = NULL; + } + } + + if (mask & KADM5_ATTRIBUTES) + entry->attributes = kdb.attributes; + + if (mask & KADM5_KVNO) + for (entry->kvno = 0, i=0; i entry->kvno) + entry->kvno = kdb.key_data[i].key_data_kvno; + + if (handle->api_version == KADM5_API_VERSION_2) + entry->mkvno = 0; + else { + /* XXX I'll be damned if I know how to deal with this one --marc */ + entry->mkvno = 1; + } + + /* + * The new fields that only exist in version 2 start here + */ + if (handle->api_version == KADM5_API_VERSION_2) { + if (mask & KADM5_MAX_RLIFE) + entry->max_renewable_life = kdb.max_renewable_life; + if (mask & KADM5_LAST_SUCCESS) + entry->last_success = kdb.last_success; + if (mask & KADM5_LAST_FAILED) + entry->last_failed = kdb.last_failed; + if (mask & KADM5_FAIL_AUTH_COUNT) + entry->fail_auth_count = kdb.fail_auth_count; + if (mask & KADM5_TL_DATA) { + krb5_tl_data td, *tl, *tl2; + + entry->n_tl_data = kdb.n_tl_data; + entry->tl_data = NULL; + + tl = kdb.tl_data; + while (tl) { + if ((tl2 = dup_tl_data(tl)) == NULL) { + ret = ENOMEM; + goto done; + } + tl2->tl_data_next = entry->tl_data; + entry->tl_data = tl2; + + tl = tl->tl_data_next; + } + + if (kdb.e_length) { + td.tl_data_type = KRB5_TL_KADM5_E_DATA; + td.tl_data_length = kdb.e_length; + td.tl_data_contents = kdb.e_data; + + if ((tl = dup_tl_data(&td)) == NULL) { + ret = ENOMEM; + goto done; + } + tl->tl_data_next = entry->tl_data; + entry->tl_data = tl; + } + } + if (mask & KADM5_KEY_DATA) { + entry->n_key_data = kdb.n_key_data; + entry->key_data = (krb5_key_data *) + malloc(entry->n_key_data*sizeof(krb5_key_data)); + if (entry->key_data == NULL) { + ret = ENOMEM; + goto done; + } + for (i = 0; i < entry->n_key_data; i++) + if (ret = krb5_copy_key_data_contents(handle->context, + &kdb.key_data[i], + &entry->key_data[i])) + goto done; + } + } + + /* + * If KADM5_API_VERSION_1, we return an allocated structure, and + * we need to convert the new structure back into the format the + * caller is expecting. + */ + if (handle->api_version == KADM5_API_VERSION_1) { + kadm5_principal_ent_t_v1 newv1; + + newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1))); + if (newv1 == NULL) { + ret = ENOMEM; + goto done; + } + + newv1->principal = entry->principal; + newv1->princ_expire_time = entry->princ_expire_time; + newv1->last_pwd_change = entry->last_pwd_change; + newv1->pw_expiration = entry->pw_expiration; + newv1->max_life = entry->max_life; + newv1->mod_name = entry->mod_name; + newv1->mod_date = entry->mod_date; + newv1->attributes = entry->attributes; + newv1->kvno = entry->kvno; + newv1->mkvno = entry->mkvno; + newv1->policy = entry->policy; + newv1->aux_attributes = entry->aux_attributes; + + *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1; + } + + ret = KADM5_OK; + +done: + if (ret && entry->principal) + krb5_free_principal(handle->context, entry->principal); + kdb_free_entry(handle, &kdb, &adb); + + return ret; +} + +/* + * Function: check_pw_reuse + * + * Purpose: Check if a key appears in a list of keys, in order to + * enforce password history. + * + * Arguments: + * + * context (r) the krb5 context + * histkey_encblock (r) the encblock that hist_key_data is + * encrypted in + * n_new_key_data (r) length of new_key_data + * new_key_data (r) keys to check against + * pw_hist_data, encrypted in histkey_encblock + * n_pw_hist_data (r) length of pw_hist_data + * pw_hist_data (r) passwords to check new_key_data against + * + * Effects: + * For each new_key in new_key_data: + * decrypt new_key with the master_encblock + * for each password in pw_hist_data: + * for each hist_key in password: + * decrypt hist_key with histkey_encblock + * compare the new_key and hist_key + * + * Returns krb5 errors, KADM5_PASS_RESUSE if a key in + * new_key_data is the same as a key in pw_hist_data, or 0. + */ +static kadm5_ret_t +check_pw_reuse(krb5_context context, + krb5_encrypt_block *histkey_encblock, + int n_new_key_data, krb5_key_data *new_key_data, + int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data) +{ + int x, y, z; + krb5_keyblock newkey, histkey; + krb5_error_code ret; + + for (x = 0; x < n_new_key_data; x++) { + if (ret = krb5_dbekd_decrypt_key_data(context, + &master_encblock, + &(new_key_data[x]), + &newkey, NULL)) + return(ret); + for (y = 0; y < n_pw_hist_data; y++) { + for (z = 0; z < pw_hist_data[y].n_key_data; z++) { + if (ret = + krb5_dbekd_decrypt_key_data(context, + histkey_encblock, + &pw_hist_data[y].key_data[z], + &histkey, NULL)) + return(ret); + + if ((newkey.length == histkey.length) && + (newkey.enctype == histkey.enctype) && + (memcmp(newkey.contents, histkey.contents, + histkey.length) == 0)) { + krb5_free_keyblock_contents(context, &histkey); + krb5_free_keyblock_contents(context, &newkey); + + return(KADM5_PASS_REUSE); + } + krb5_free_keyblock_contents(context, &histkey); + } + } + krb5_free_keyblock_contents(context, &newkey); + } + + return(0); +} + +/* + * Function: create_history_entry + * + * Purpose: Creates a password history entry from an array of + * key_data. + * + * Arguments: + * + * context (r) krb5_context to use + * n_key_data (r) number of elements in key_data + * key_data (r) keys to add to the history entry + * hist (w) history entry to fill in + * + * Effects: + * + * hist->key_data is allocated to store n_key_data key_datas. Each + * element of key_data is decrypted with master_encblock, re-encrypted + * in hist_encblock, and added to hist->key_data. hist->n_key_data is + * set to n_key_data. + */ +int create_history_entry(krb5_context context, int n_key_data, + krb5_key_data *key_data, osa_pw_hist_ent *hist) +{ + int i, ret; + krb5_keyblock key; + krb5_keysalt salt; + + hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data)); + if (hist->key_data == NULL) + return ENOMEM; + memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data)); + + for (i = 0; i < n_key_data; i++) { + if (ret = krb5_dbekd_decrypt_key_data(context, + &master_encblock, + &key_data[i], + &key, &salt)) + return ret; + if (ret = krb5_dbekd_encrypt_key_data(context, + &hist_encblock, + &key, &salt, + key_data[i].key_data_kvno, + &hist->key_data[i])) + return ret; + krb5_free_keyblock_contents(context, &key); + /* krb5_free_keysalt(context, &salt); */ + } + + hist->n_key_data = n_key_data; + return 0; +} + +int free_history_entry(krb5_context context, osa_pw_hist_ent *hist) +{ + int i; + + for (i = 0; i < hist->n_key_data; i++) + krb5_free_key_data_contents(context, &hist->key_data[i]); + free(hist->key_data); +} + +/* + * Function: add_to_history + * + * Purpose: Adds a password to a principal's password history. + * + * Arguments: + * + * context (r) krb5_context to use + * adb (r/w) admin principal entry to add keys to + * pol (r) adb's policy + * pw (r) keys for the password to add to adb's key history + * + * Effects: + * + * add_to_history adds a single password to adb's password history. + * pw contains n_key_data keys in its key_data, in storage should be + * allocated but not freed by the caller (XXX blech!). + * + * This function maintains adb->old_keys as a circular queue. It + * starts empty, and grows each time this function is called until it + * is pol->pw_history_num items long. adb->old_key_len holds the + * number of allocated entries in the array, and must therefore be [0, + * pol->pw_history_num). adb->old_key_next is the index into the + * array where the next element should be written, and must be [0, + * adb->old_key_len). + */ +static kadm5_ret_t add_to_history(krb5_context context, + osa_princ_ent_t adb, + kadm5_policy_ent_t pol, + osa_pw_hist_ent *pw) +{ + osa_pw_hist_ent hist, *histp; + int ret, i; + + /* A history of 1 means just check the current password */ + if (pol->pw_history_num == 1) + return 0; + + /* resize the adb->old_keys array if necessary */ + if (adb->old_key_len < pol->pw_history_num-1) { + adb->old_keys = (osa_pw_hist_ent *) + realloc(adb->old_keys, + (adb->old_key_len+1)*sizeof(osa_pw_hist_ent)); + if (adb->old_keys == NULL) + return(ENOMEM); + + memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent)); + adb->old_key_len++; + } + + /* free the old pw history entry if it contains data */ + histp = &adb->old_keys[adb->old_key_next]; + for (i = 0; i < histp->n_key_data; i++) + krb5_free_key_data_contents(context, &histp->key_data[i]); + + /* store the new entry */ + adb->old_keys[adb->old_key_next] = *pw; + + /* update the next pointer */ + if (++adb->old_key_next == pol->pw_history_num-1) + adb->old_key_next = 0; + + return(0); +} + +kadm5_ret_t +kadm5_chpass_principal(void *server_handle, + krb5_principal principal, char *password) +{ + krb5_int32 now; + kadm5_policy_ent_rec pol; + osa_princ_ent_rec adb; + krb5_db_entry kdb, kdb_save; + int ret, ret2, last_pwd, i, hist_added; + int have_pol = 0; + kadm5_server_handle_t handle = server_handle; + osa_pw_hist_ent hist; + + CHECK_HANDLE(server_handle); + + hist_added = 0; + memset(&hist, 0, sizeof(hist)); + + if (principal == NULL || password == NULL) + return EINVAL; + if ((krb5_principal_compare(handle->context, + principal, hist_princ)) == TRUE) + return KADM5_PROTECT_PRINCIPAL; + + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + + /* we are going to need the current keys after the new keys are set */ + if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) { + kdb_free_entry(handle, &kdb, &adb); + return(ret); + } + + if ((adb.aux_attributes & KADM5_POLICY)) { + if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol))) + goto done; + have_pol = 1; + } + + if ((ret = passwd_check(handle, password, adb.aux_attributes & + KADM5_POLICY, &pol, principal))) + goto done; + + if (ret = krb5_dbe_cpw(handle->context, &master_encblock, + handle->params.keysalts, + handle->params.num_keysalts, + password, 0 /* increment kvno */, &kdb)) + goto done; + + kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; + + if (ret = krb5_timeofday(handle->context, &now)) + goto done; + + if ((adb.aux_attributes & KADM5_POLICY)) { + /* the policy was loaded before */ + + if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, + &kdb, &last_pwd)) + goto done; + +#if 0 + /* + * The spec says this check is overridden if the caller has + * modify privilege. The admin server therefore makes this + * check itself (in chpass_principal_wrapper, misc.c). A + * local caller implicitly has all authorization bits. + */ + if ((now - last_pwd) < pol.pw_min_life && + !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + ret = KADM5_PASS_TOOSOON; + goto done; + } +#endif + + if (ret = create_history_entry(handle->context, + kdb_save.n_key_data, + kdb_save.key_data, &hist)) + goto done; + + if (ret = check_pw_reuse(handle->context, + &hist_encblock, + kdb.n_key_data, kdb.key_data, + 1, &hist)) + goto done; + + if (pol.pw_history_num > 1) { + if (adb.admin_history_kvno != hist_kvno) { + ret = KADM5_BAD_HIST_KEY; + goto done; + } + + if (ret = check_pw_reuse(handle->context, + &hist_encblock, + kdb.n_key_data, kdb.key_data, + adb.old_key_len, adb.old_keys)) + goto done; + + if (ret = add_to_history(handle->context, &adb, &pol, &hist)) + goto done; + hist_added = 1; + } + + if (pol.pw_max_life) + kdb.pw_expiration = now + pol.pw_max_life; + else + kdb.pw_expiration = 0; + } else { + kdb.pw_expiration = 0; + } + + if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) + goto done; + + if ((ret = kdb_put_entry(handle, &kdb, &adb))) + goto done; + + ret = KADM5_OK; +done: + if (!hist_added && hist.key_data) + free_history_entry(handle->context, &hist); + kdb_free_entry(handle, &kdb, &adb); + kdb_free_entry(handle, &kdb_save, NULL); + krb5_dbe_free_contents(handle->context, &kdb); + + if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol)) + && !ret) + ret = ret2; + + return ret; +} + +kadm5_ret_t +kadm5_randkey_principal(void *server_handle, + krb5_principal principal, + krb5_keyblock **keyblocks, + int *n_keys) +{ + krb5_db_entry kdb; + osa_princ_ent_rec adb; + krb5_int32 now; + kadm5_policy_ent_rec pol; + krb5_key_data *key_data; + krb5_keyblock *keyblock; + int ret, last_pwd, have_pol = 0; + kadm5_server_handle_t handle = server_handle; + + if (keyblocks) + *keyblocks = NULL; + + CHECK_HANDLE(server_handle); + + if (principal == NULL) + return EINVAL; + if (hist_princ && /* this will be NULL when initializing the databse */ + ((krb5_principal_compare(handle->context, + principal, hist_princ)) == TRUE)) + return KADM5_PROTECT_PRINCIPAL; + + if ((ret = kdb_get_entry(handle, principal, &kdb, &adb))) + return(ret); + + if (ret = krb5_dbe_crk(handle->context, &master_encblock, + handle->params.keysalts, + handle->params.num_keysalts, + &kdb)) + goto done; + + kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; + + if (ret = krb5_timeofday(handle->context, &now)) + goto done; + + if ((adb.aux_attributes & KADM5_POLICY)) { + if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, + &pol)) != KADM5_OK) + goto done; + have_pol = 1; + + if (ret = krb5_dbe_lookup_last_pwd_change(handle->context, + &kdb, &last_pwd)) + goto done; + +#if 0 + /* + * The spec says this check is overridden if the caller has + * modify privilege. The admin server therefore makes this + * check itself (in chpass_principal_wrapper, misc.c). A + * local caller implicitly has all authorization bits. + */ + if((now - last_pwd) < pol.pw_min_life && + !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + ret = KADM5_PASS_TOOSOON; + goto done; + } +#endif + + if(pol.pw_history_num > 1) { + if(adb.admin_history_kvno != hist_kvno) { + ret = KADM5_BAD_HIST_KEY; + goto done; + } + + if (ret = check_pw_reuse(handle->context, + &hist_encblock, + kdb.n_key_data, kdb.key_data, + adb.old_key_len, adb.old_keys)) + goto done; + } + if (pol.pw_max_life) + kdb.pw_expiration = now + pol.pw_max_life; + else + kdb.pw_expiration = 0; + } else { + kdb.pw_expiration = 0; + } + + if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) + goto done; + + if (keyblocks) { + if (handle->api_version == KADM5_API_VERSION_1) { + /* Version 1 clients will expect to see a DES_CRC enctype. */ + if (ret = krb5_dbe_find_enctype(handle->context, &kdb, + ENCTYPE_DES_CBC_CRC, + -1, -1, &key_data)) + goto done; + + if (ret = decrypt_key_data(handle->context, 1, key_data, + keyblocks, NULL)) + goto done; + } else { + ret = decrypt_key_data(handle->context, + kdb.n_key_data, kdb.key_data, + keyblocks, n_keys); + if (ret) + goto done; + } + } + + if ((ret = kdb_put_entry(handle, &kdb, &adb))) + goto done; + + ret = KADM5_OK; +done: + kdb_free_entry(handle, &kdb, &adb); + if (have_pol) + kadm5_free_policy_ent(handle->lhandle, &pol); + + return ret; +} + +/* + * Allocate an array of n_key_data krb5_keyblocks, fill in each + * element with the results of decrypting the nth key in key_data with + * master_encblock, and if n_keys is not NULL fill it in with the + * number of keys decrypted. + */ +static int decrypt_key_data(krb5_context context, + int n_key_data, krb5_key_data *key_data, + krb5_keyblock **keyblocks, int *n_keys) +{ + krb5_keyblock *keys; + int ret, i; + + keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock)); + if (keys == NULL) + return ENOMEM; + memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); + + for (i = 0; i < n_key_data; i++) { + if (ret = krb5_dbekd_decrypt_key_data(context, + &master_encblock, + &key_data[i], + &keys[i], NULL)) { + + memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock)); + free(keys); + return ret; + } + } + + *keyblocks = keys; + if (n_keys) + *n_keys = n_key_data; + + return 0; +} + +/* + * Function: kadm5_decrypt_key + * + * Purpose: Retrieves and decrypts a principal key. + * + * Arguments: + * + * server_handle (r) kadm5 handle + * entry (r) principal retrieved with kadm5_get_principal + * ktype (r) enctype to search for, or -1 to ignore + * stype (r) salt type to search for, or -1 to ignore + * kvno (r) kvno to search for, -1 for max, 0 for max + * only if it also matches ktype and stype + * keyblock (w) keyblock to fill in + * keysalt (w) keysalt to fill in, or NULL + * kvnop (w) kvno to fill in, or NULL + * + * Effects: Searches the key_data array of entry, which must have been + * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to + * find a key with a specified enctype, salt type, and kvno in a + * principal entry. If not found, return ENOENT. Otherwise, decrypt + * it with the master key, and return the key in keyblock, the salt + * in salttype, and the key version number in kvno. + * + * If ktype or stype is -1, it is ignored for the search. If kvno is + * -1, ktype and stype are ignored and the key with the max kvno is + * returned. If kvno is 0, only the key with the max kvno is returned + * and only if it matches the ktype and stype; otherwise, ENOENT is + * returned. + */ +kadm5_ret_t kadm5_decrypt_key(void *server_handle, + kadm5_principal_ent_t entry, krb5_int32 + ktype, krb5_int32 stype, krb5_int32 + kvno, krb5_keyblock *keyblock, + krb5_keysalt *keysalt, int *kvnop) +{ + kadm5_server_handle_t handle = server_handle; + krb5_db_entry dbent; + krb5_key_data *key_data; + int ret; + + CHECK_HANDLE(server_handle); + + if (entry->n_key_data == 0 || entry->key_data == NULL) + return EINVAL; + + /* find_enctype only uses these two fields */ + dbent.n_key_data = entry->n_key_data; + dbent.key_data = entry->key_data; + if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype, + stype, kvno, &key_data)) + return ret; + + if (ret = krb5_dbekd_decrypt_key_data(handle->context, + &master_encblock, key_data, + keyblock, keysalt)) + return ret; + + if (kvnop) + *kvnop = key_data->key_data_kvno; + + return KADM5_OK; +} diff --git a/src/lib/kadm5/unit-test/ChangeLog b/src/lib/kadm5/unit-test/ChangeLog new file mode 100644 index 000000000..172084c20 --- /dev/null +++ b/src/lib/kadm5/unit-test/ChangeLog @@ -0,0 +1,18 @@ +Thu Jul 18 20:18:30 1996 Marc Horowitz + + * api.2/init-v2.exp (test150, test151): -s flag is now -S + +Mon Jul 8 17:00:26 1996 Barry Jaspan + + * api.2/init-v2.exp: admin databases must now be created before + use (not created implicitly) + + * api.1/lock.exp: lock-test 13 should be a warning, not a failure. + + * api.0/rename-principal.exp: fix rename test to create principal + with correct salt first, and check explicitly for NO_RENAME_SALT + when appropriate + + * lib.t: add create_principal_with_keysalts + + diff --git a/src/lib/kadm5/unit-test/Makefile.ov b/src/lib/kadm5/unit-test/Makefile.ov new file mode 100644 index 000000000..208bedeb3 --- /dev/null +++ b/src/lib/kadm5/unit-test/Makefile.ov @@ -0,0 +1,154 @@ +TOP = ../../../kadmin +include $(TOP)/config.mk/template + +CFLAGS := $(CFLAGS) -DUSE_KADM5_API_VERSION=1 + +LIBS := $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBRPCLIB) $(LIBDYN) \ + $(LIBGSSAPI_KRB5) $(LIBKDB5) \ + $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBTCL) $(LIBM) \ + $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB) + +INIT_SRCS = init-test.c ../client_init.c +DESTROY_SRCS = destroy-test.c +HANDLE_SRCS = handle-test.c +RANDKEY_SRCS = randkey-test.c +ITER_SRCS = iter-test.c +LOCKTEST_SRCS = lock-test.c +SIZE_SRCS = sizes-test.c + +PROG = init-test +SRCS = $(INIT_SRCS) +OBJS = init-test.o client_init.o + +client_init.o: ../client_init.c + $(CC) $(CFLAGS) -DUSE_KADM5_API_VERSION=2 -DINIT_TEST -c -I.. $< + +expand Program + +PROG = destroy-test +SRCS = $(DESTROY_SRCS) +OBJS = destroy-test.o + +expand Program + +PROG = client-handle-test +SRCS = $(HANDLE_SRCS) +OBJS = handle-test.o + +expand Program + +PROG = client-iter-test +SRCS = $(ITER_SRCS) +OBJS = iter-test.o + +expand Program + +LIBS := $(LIBADMSRV) $(LIBCOM_ERR) $(LIBRPCLIB) $(LIBDYN) \ + $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5) \ + $(LIBCRYPTO) $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) \ + $(NDBMLIB) $(BSDLIB) $(NETLIB) + +PROG = randkey-test +SRCS = $(RANDKEY_SRCS) +OBJS = randkey-test.o + +expand Program + +PROG = server-handle-test +SRCS = $(HANDLE_SRCS) +OBJS = handle-test.o + +expand Program + +PROG = lock-test +SRCS = $(LOCKTEST_SRCS) +OBJS = lock-test.o + +expand Program + +LIBS := $(LIBS) $(REGEXLIB) +PROG = server-iter-test +SRCS = $(ITER_SRCS) +OBJS = iter-test.o + +expand Program + +PROG = sizes-test +SRCS = $(SIZE_SRCS) +OBJS = sizes-test.o + +expand Program + +SRCS = $(INIT_SRCS) $(DESTROY_SRCS) $(HANDLE_SRCS) \ + $(RANDKEY_SRCS) $(LOCKTEST_SRCS) $(ITER_SRCS) \ + $(SIZE_SRCS) + +expand Depend + +unit-test:: unit-test-client unit-test-server + +unit-test-client: unit-test-client-setup unit-test-client-body \ + unit-test-client-cleanup + +unit-test-server: unit-test-server-setup unit-test-server-body \ + unit-test-server-cleanup + +test-randkey:: randkey-test + ./randkey-test + +test-handle-server:: server-handle-test + ./server-handle-test + +test-handle-client:: client-handle-test + ./client-handle-test + +test-noauth: init-test + ./init-test + +test-destroy: destroy-test + ./destroy-test + +test-sizes: sizes-test + ./sizes-test + +unit-test-client-setup:: + $(START_SERVERS) + +unit-test-client-cleanup:: + $(STOP_SERVERS) + +unit-test-server-setup:: + $(START_SERVERS_LOCAL) + +unit-test-server-cleanup:: + $(STOP_SERVERS_LOCAL) + +capi.0: api.0 + -rm -f capi.0 + ln -s api.0 capi.0 + +capi.2: api.2 + -rm -f capi.2 + ln -s api.2 capi.2 + +unit-test-client-body: capi.0 capi.2 site.exp test-noauth test-destroy test-handle-client test-sizes + $(RUNTEST) --tool capi API=$(CLNTTCL) KDBFIVE_EDIT=$(TOP)/../admin/edit/kdb5_edit KINIT=$(TOP)/../clients/kinit/kinit KDESTROY=$(TOP)/../clients/kdestroy/kdestroy KADMIN_LOCAL=$(TOP)/cli/kadmin.local TOP=$(TOP) RPC=1 + +sapi.0: api.0 + -rm -f sapi.0 + ln -s api.0 sapi.0 + +sapi.1: api.1 + -rm -f sapi.1 + ln -s api.1 sapi.1 + +sapi.2: api.2 + -rm -f sapi.2 + ln -s api.2 sapi.2 + +unit-test-server-body: sapi.0 sapi.1 sapi.2 site.exp randkey-test test-handle-server \ + lock-test test-sizes + $(RUNTEST) --tool sapi API=$(SRVTCL) LOCKTEST=./lock-test KDBFIVE_EDIT=$(TOP)/../admin/edit/kdb5_edit KADMIN_LOCAL=$(TOP)/cli/kadmin.local TOP=$(TOP) RPC=0 + +clean:: + $(CLEAN) -r *.log *.plog *.sum *.psum unit-test-log.* diff --git a/src/lib/kadm5/unit-test/api.0/chpass-principal.exp b/src/lib/kadm5/unit-test/api.0/chpass-principal.exp new file mode 100644 index 000000000..12fa3b9d1 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/chpass-principal.exp @@ -0,0 +1,176 @@ +source lib.t +api_exit +api_start + +test "chpass-principal 180" +proc test180 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_chpass_principal $server_handle "%s/a" FoobarBax + } $test] + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test180 } + +test "chpass-principal 180.5" +proc test1805 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_chpass_principal $server_handle "%s/a" FoobarBax + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test1805 } + +# +# admin with changepw service tickets try to change other principals +# password, failes with AUTH error +test "chpass-principal 180.625" +proc test180625 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_chpass_principal $server_handle "%s/a" password + } $test] "AUTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test180625 } + +test "chpass-principal 180.75" +proc test18075 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_chpass_principal $server_handle "%s/a" Foobar + } $test] "AUTH_CHANGEPW" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test18075 } + +test "chpass-principal 182" +proc test182 {} { + global test + + if { ! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_chpass_principal $server_handle kadmin/history password + } "PROTECT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test182 + +test "chpass-principal 183" +proc test183 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if { ! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_chpass_principal null "%s/a" password + } $test] "BAD_SERVER_HANDLE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test183 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/crte-policy.exp b/src/lib/kadm5/unit-test/api.0/crte-policy.exp new file mode 100644 index 000000000..dbf4f1cbc --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/crte-policy.exp @@ -0,0 +1,991 @@ +source lib.t +api_exit +api_start + +# Description: (1) Fails for mask with undefined bit set. +# 01/24/94: pshuang: untried. +test "create-policy 1" +proc test1 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + 0xF01000 + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test1 + +# Description: (2) Fails if caller connected with CHANGEPW_SERVICE. +test "create-policy 2" +proc test2 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy"; + return + } +} +if {$RPC} { test2 } + +# Description: (3) Fails for mask without POLICY bit set. +# 01/24/94: pshuang: untried. +test "create-policy 3" +proc test3 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + 0x000000 + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3 + +# Description: (4) Fails for mask with REF_COUNT bit set. +test "create-policy 4" +proc test4 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_REF_COUNT} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + +# Description: (5) Fails for invalid policy name. +# 01/24/94: pshuang: untried. +test "create-policy 5" +proc test5 {} { + global test + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/"] \ + {OVSEC_KADM_POLICY} + } $test] "BAD_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +# Description: (6) Fails for existing policy name. +test "create-policy 6" +proc test6 {} { + global test +# set prms_id 777 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_create_policy $server_handle [simple_policy test-pol] \ + {OVSEC_KADM_POLICY} + } "DUP" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test6 + +# Description: (7) Fails for null policy name. +# 01/24/94: pshuang: untried. +test "create-policy 7" +proc test7 {} { + global test +# set prms_id 1977 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_create_policy $server_handle [simple_policy null] \ + {OVSEC_KADM_POLICY} + } "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test7 + +# Description: (8) Fails for empty-string policy name. +test "create-policy 8" +proc test8 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_create_policy $server_handle [simple_policy ""] \ + {OVSEC_KADM_POLICY} + } "BAD_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +# Description: (9) Accepts 0 for pw_min_life. +test "create-policy 9" +proc test9 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LIFE} + } $test]]} { + fail "$test: create failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +# Description: (10) Accepts non-zero for pw_min_life. +test "create-policy 10" +proc test10 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 32 0 0 0 0 0 } \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +# Description: (11) Accepts 0 for pw_max_life. +test "create-policy 11" +proc test11 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MAX_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +# Description: (12) Accepts non-zero for pw_max_life. +test "create-policy 12" +proc test12 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 32 0 0 0 0 } \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MAX_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +# Description: (13) Rejects 0 for pw_min_length. +test "create-policy 13" +proc test13 {} { + global test + global prompt + + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LENGTH} + } $test] "BAD_LENGTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +# Description: (14) Accepts non-zero for pw_min_length. +test "create-policy 14" +proc test14 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 8 0 0 0 } \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LENGTH} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 3\n" + expect { + -re "8\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +# Description: (15) Rejects 0 for pw_min_classes. +test "create-policy 15" +proc test15 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +# Description: (16) Accepts 1 for pw_min_classes. +test "create-policy 16" +proc test16 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 1 0 0 } \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +# Description: (17) Accepts 4 for pw_min_classes. +test "create-policy 17" +proc test17 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 5 0 0} \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "5\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +# Description: (18) Rejects 5 for pw_min_classes. +test "create-policy 18" +proc test18 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 6 0 0} \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +# Description: (19) Rejects 0 for pw_history_num. +test "create-policy 19" +proc test19 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +# Description: (20) Accepts 1 for pw_history_num. +test "create-policy 20" +proc test20 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 0 1 0} \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +# Description: (21) Accepts 10 for pw_history_num. +test "create-policy 21" +proc test21 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 0 10 0} \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "10\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +# Description: (21.5) Rejects 11 for pw_history_num. +# 01/24/94: pshuang: untried. + +test "create-policy 21.5" +proc test215 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle {"%s/a" 0 0 0 0 11 0} \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test215 + + +# Description: (22) Fails for user with no access bits. +test "create-policy 22" +proc test22 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test22 + +# Description: (23) Fails for user with "get" but not "add". +test "create-policy 23" +proc test23 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test23 + +# Description: (24) Fails for user with "modify" but not "add". +# 01/24/94: pshuang: untried. +test "create-policy 24" +proc test24 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test24 + +# Description: (25) Fails for user with "delete" but not "add". +# 01/24/94: pshuang: untried. +test "create-policy 25" +proc test25 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test25 + +# Description: Succeeds for user with "add". +test "create-policy 26" +proc test26 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +# Description: Succeeds for user with "get" and "add". +# 01/24/94: pshuang: untried. +test "create-policy 27" +proc test27 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get-add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test27 + +# Description: (28) Rejects null policy argument. +# 01/24/94: pshuang: untried. +test "create-policy 28" +proc test28 {} { + global test + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_create_policy $server_handle null {OVSEC_KADM_POLICY} + } "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "create-policy 30" +proc test30 {} { + global test + one_line_fail_test [format { + ovsec_kadm_create_policy null [simple_policy "%s/a"] \ + {OVSEC_KADM_POLICY} + } $test] "BAD_SERVER_HANDLE" +} +test30 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/crte-principal.exp b/src/lib/kadm5/unit-test/api.0/crte-principal.exp new file mode 100644 index 000000000..12c300793 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/crte-principal.exp @@ -0,0 +1,1330 @@ +source lib.t +api_exit +api_start + +#test "create-principal 1" +# +#proc test1 {} { +# global test +# begin_dump +# one_line_fail_test [format { +# ovsec_kadm_create_principal $server_handle \ +# [simple_principal "%s/a"] {OVSEC_KADM_PRINCIPAL} "%s/a" +# } $test $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test1 + +test "create-principal 2" + +proc test2 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_create_principal $server_handle null \ + {OVSEC_KADM_PRINCIPAL} testpass + } "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test2 + +test "create-principal 3" +proc test3 {} { + global test +# set prms_id 777 +# setup_xfail {*-*-*} $prms_id + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} null + } $test] "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test3 + +test "create-principal 4" +proc test4 {} { + global test + + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} "" + } $test] "_Q_TOOSHORT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test4 + +test "create-principal 5" +proc test5 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle \ + [simple_principal "%s/a"] {0x100001} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test5 + +test "create-principal 6" +proc test6 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_LAST_PWD_CHANGE} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test6 + +test "create-principal 7" +proc test7 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MOD_TIME} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test7 + +test "create-principal 8" +proc test8 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MOD_NAME} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test8 + +test "create-principal 9" +proc test9 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MKVNO} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test9 + +test "create-principal 10" +proc test10 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_AUX_ATTRIBUTES} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test10 + +test "create-principal 11" +proc test11 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_POLICY_CLR} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test11 + +test "create-principal 12" +proc test12 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" + +} +if {$RPC} { test12 } + +test "create-principal 13" +proc test13 {} { + global test + begin_dump + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test13 } + +test "create-principal 14" +proc test14 {} { + global test + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test14 } + +test "create-principal 15" +proc test15 {} { + global test + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test15 } + +test "create-principal 16" +proc test16 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test16 } + +test "create-principal 17" +proc test17 {} { + global test + + begin_dump + if {! (( [principal_exists "$test/a"]) || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} testpass + } $test] "DUP" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test17 + +test "create-principal 18" +proc test18 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} tP + } $test] "_Q_TOOSHORT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test18 + +test "create-principal 19" +proc test19 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} testpassword + } $test] "_Q_CLASS" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test19 + +test "create-principal 20" +proc test20 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} Abyssinia + } $test] "_Q_DICT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test20 + +test "create-principal 21" +proc test21 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" non-existant-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} NotinTheDictionary + } $test] "UNK_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test21 + +test "create-principal 23" +proc test23 {} { + global test + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + one_line_succeed_test \ + [format {ovsec_kadm_get_principal $server_handle "%s/a" p} $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test23 + +test "create-principal 24" +proc test24 {} { + global test + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/rename admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + one_line_succeed_test \ + [format {ovsec_kadm_get_principal $server_handle "%s/a" p} $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test24 } + + +test "create-principal 28" +proc test28 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "create-principal 29" +proc test29 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_PRINC_EXPIRE_TIME} \ + inTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 1\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test29 + +test "create-principal 30" +proc test30 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "create-principal 31" +proc test31 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol-nopw] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY \ + OVSEC_KADM_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "create-principal 32" +proc test32 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY \ + OVSEC_KADM_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "create-principal 33" +proc test33 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + {"%s/a" 0 0 1234 0 null 0 0 0 0 null 0} \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test33 + +test "create-principal 34" +proc test34 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + { "%s/a" 0 0 1234 0 null 0 0 0 0 test-pol-nopw 0} \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY \ + OVSEC_KADM_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test34 + +test "create-principal 35" +proc test35 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + {"%s/a" 0 0 1234 0 null 0 0 0 0 test-pol 0} \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY \ + OVSEC_KADM_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test35 + +test "create-principal 36" +proc test36 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle \ + {"%s/a" 0 0 999999999 0 null 0 0 0 0 test-pol 0} \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY \ + OVSEC_KADM_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol policy} ]} { + error_and_restart "$test: cannot retrieve policy" + return + } + + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test36 + +test "create-principal 37" +proc test37 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test37 + +test "create-principal 38" +proc test38 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test38 + +test "create-principal 39" +proc test39 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if { ! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: cannot not retrieve principal" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test39 + +test "create-principal 40" +proc test40 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 4\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test40 + +test "create-principal 43" +proc test43 {} { + global test + one_line_fail_test [format { + ovsec_kadm_create_principal null \ + [simple_principal "%s/a"] {OVSEC_KADM_PRINCIPAL} "%s/a" + } $test $test] "BAD_SERVER_HANDLE" +} +test43 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/destroy.exp b/src/lib/kadm5/unit-test/api.0/destroy.exp new file mode 100644 index 000000000..31b844786 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/destroy.exp @@ -0,0 +1,203 @@ +source lib.t +api_exit +api_start + +test "destroy 1" + +proc test1 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_destroy $server_handle} + end_dump_compare "no-diffs" +} +test1 + +#test "destroy 2" +# +#proc test2 {} { +# global test +# begin_dump +# if {! [cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test \ +# {ovsec_kadm_get_principal $server_handle admin principal} \ +# "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test2 + +#test "destroy 3" +#proc test3 {} { +# global test +# +# begin_dump +# if {! (( ! [principal_exists "$test/a"]) || [delete_principal "$test/a"])} { +# error_and_restart "$test couldn't delete principal \"$test/a\"" +# return +# } +# if {! [cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# ovsec_kadm_create_principal $server_handle \ +# [simple_principal "%s/a"] {OVSEC_KADM_PRINCIPAL} "%s/a" +# } $test $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test3 + +#test "destroy 4" +#proc test4 {} { +# global test prompt +# +# if {! (([principal_exists "$test/a"]) || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! ([cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }] && +# [cmd [format { +# ovsec_kadm_get_principal $server_handle "%s/a" principal +# } $test]])} { +# error_and_restart "$test: error getting principal" +# return; +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# ovsec_kadm_modify_principal $server_handle \ +# {"%s/a" 0 0 0 0 0 0 0 %d 0 0 0} {OVSEC_KADM_KVNO} +# } $test "77"] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test4 + +#test "destroy 5" +# +#proc test5 {} { +# global test +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! [cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# ovsec_kadm_delete_principal $server_handle "%s/a" +# } $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test5 + +#test "destroy 6" +# +#proc test6 {} { +# global test +# begin_dump +# one_line_fail_test {ovsec_kadm_destroy $server_handle} "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test6 + + +#test "destroy 7" +# +#proc test7 {} { +# global test +# begin_dump +# if {! [cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }]} { +# error "$test: unexpected failure in init" +# return +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +# one_line_fail_test {ovsec_kadm_destroy $server_handle} "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test7 + +test "destroy 8" +proc test8 {} { + global test + begin_dump + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } + one_line_succeed_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } + end_dump_compare "no-diffs" +} +test8 + +test "destroy 9" +proc test9 {} { + global test + one_line_fail_test {ovsec_kadm_destroy null} "BAD_SERVER_HANDLE" +} +test9 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/dlte-policy.exp b/src/lib/kadm5/unit-test/api.0/dlte-policy.exp new file mode 100644 index 000000000..7f349b02c --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/dlte-policy.exp @@ -0,0 +1,207 @@ +source lib.t +api_exit +api_start + +test "delete-policy 2" +proc test2 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {ovsec_kadm_delete_policy $server_handle ""} "BAD_POL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test2 + +test "delete-policy 5" +proc test5 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} test5 + +test "delete-policy 6" +proc test6 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} test6 + +test "delete-policy 7" +proc test7 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test7 + +test "delete-policy 10" +proc test10 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_delete_policy $server_handle "%s/a" + } $test]]} { + fail "$test" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if { [policy_exists "$test/a"]} { + fail "$test" + return + } +} +test10 + +test "delete-policy 12" +proc test12 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test unexecpted failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_create_principal $server_handle [princ_w_pol "%s/a" \ + "%s/a"] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} \ + NotinTheDictionary + } $test $test]]} { + fail "$test: can not create principal" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {ovsec_kadm_delete_policy $server_handle test-pol} "POLICY_REF" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "delete-policy 13" +proc test13 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_policy null "%s/a" + } $test] "BAD_SERVER_HANDLE" +} +test13 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/dlte-principal.exp b/src/lib/kadm5/unit-test/api.0/dlte-principal.exp new file mode 100644 index 000000000..bb52301df --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/dlte-principal.exp @@ -0,0 +1,329 @@ +source lib.t + +api_exit +api_start + +#test "delete-principal 1" +#proc test1 {} { +# global test +# one_line_fail_test [format { +# ovsec_kadm_delete_principal $server_handle "%s/a" +# } $test] "NOT_INIT" +#} +#test1 + +test "delete-principal 2" +proc test2 {} { + global test + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {ovsec_kadm_delete_principal $server_handle null} "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: unexpected failure in destroy" + return + } +} +test2 + +test "delete-principal 5" +proc test5 {} { + global test + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "UNK_PRINC" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +test "delete-principal 6" +proc test6 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test6 } + + +test "delete-principal 7" +proc test7 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test7 } + + +test "delete-principal 8" +proc test8 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test8 } + +test "delete-principal 9" +proc test9 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test9 } + +test "delete-principal 10" +proc test10 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test10 } + +test "delete-principal 11" +proc test11 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test]]} { + fail "$test: delete failed" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if { [principal_exists "$test/a"] } { + fail "$test" + return + } +} +test11 + +test "delete-principal 12" +proc test12 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd [format { + ovsec_kadm_delete_principal $server_handle "%s/a" + } $test]]} { + fail "$test: delete failed" + return + } + if { [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test]]} { + fail "$test: principal still exists" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref - 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + pass "$test" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} + +test12 + +test "delete-principal 13" +proc test13 {} { + global test + one_line_fail_test [format { + ovsec_kadm_delete_principal null "%s/a" + } $test] "BAD_SERVER_HANDLE" +} +test13 + +return "" + + + + + diff --git a/src/lib/kadm5/unit-test/api.0/get-policy.exp b/src/lib/kadm5/unit-test/api.0/get-policy.exp new file mode 100644 index 000000000..329e7886a --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/get-policy.exp @@ -0,0 +1,199 @@ +source lib.t +api_exit +api_start + +test "get-policy 3" +proc test3 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {ovsec_kadm_get_policy $server_handle "" p} "BAD_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3 + +test "get-policy 6" +proc test6 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {ovsec_kadm_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test6 + +test "get-policy 7" +proc test7 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {ovsec_kadm_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test7 + +test "get-policy 11" +proc test11 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/get-pol StupidAdmin $OVSEC_KADM_ADMIN_SERVICE \ + null $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_policy $server_handle test-pol p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "get-policy 12" +proc test12 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/get-pol StupidAdmin \ + $OVSEC_KADM_CHANGEPW_SERVICE null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "get-policy 15" +proc test15 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/pol StupidAdmin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "get-policy 16" +proc test16 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/pol StupidAdmin $OVSEC_KADM_CHANGEPW_SERVICE \ + null $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +test "get-policy 17" +proc test17 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_policy $server_handle test-pol p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "get-policy 18" +proc test18 {} { + global test + + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {ovsec_kadm_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test18 + +test "get-policy 21" +proc test21 {} { + global test + + one_line_fail_test {ovsec_kadm_get_policy null "pol1" p} "BAD_SERVER_HANDLE" +} +test21 diff --git a/src/lib/kadm5/unit-test/api.0/get-principal.exp b/src/lib/kadm5/unit-test/api.0/get-principal.exp new file mode 100644 index 000000000..05937055e --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/get-principal.exp @@ -0,0 +1,346 @@ +source lib.t +api_exit +api_start + +test "get-principal 1" +proc test1 {} { + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {ovsec_kadm_get_principal $server_handle null p} "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test1 + +test "get-principal 2" +proc test2 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "UNK_PRINC" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test2 + +test "get-principal 3" +proc test3 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test3 } + +test "get-principal 4" +proc test4 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test4 } + +test "get-principal 5" +proc test5 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test5 } + +test "get-principal 6" +proc test6 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test6 } + +test "get-principal 7" +proc test7 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test7 } + + +test "get-principal 8" +proc test8 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_get_principal $server_handle "%s/a" p + } $test] "AUTH_GET" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test8 } + + +test "get-principal 9" +proc test9 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_principal $server_handle admin/none p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "get-principal 10" +proc test10 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_principal $server_handle admin/none p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +test "get-principal 11" +proc test11 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_principal $server_handle admin/get p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "get-principal 12" +proc test12 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_principal $server_handle admin/get p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "get-principal 13" +proc test13 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_principal $server_handle admin/add p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "get-principal 14" +proc test14 {} { + global test + if {! [cmd { + ovsec_kadm_init admin/get-mod admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {ovsec_kadm_get_principal $server_handle admin/add p} + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +test "get-principal 15" +proc test15 {} { + one_line_fail_test \ + {ovsec_kadm_get_principal null "admin" p} "BAD_SERVER_HANDLE" +} +test15 + +return "" + + + + diff --git a/src/lib/kadm5/unit-test/api.0/init.exp b/src/lib/kadm5/unit-test/api.0/init.exp new file mode 100644 index 000000000..5df5dcfc9 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/init.exp @@ -0,0 +1,727 @@ +source lib.t + +# Assumptions: +# +# Principal "admin" exists, with "get", "add", "modify" and "delete" +# access bits and password "admin". +# The string "not-the-password" isn't the password of any user in the database. +# Database master password is "mrroot". + +api_exit +api_start +test "init 1" + +one_line_fail_test_nochk \ + {ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE "" \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle} + +test "init 2" + +one_line_fail_test_nochk \ + {ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE @ \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle} + +test "init 2.5" + +one_line_fail_test_nochk \ + {ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE BAD.REALM \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle} + +test "init 3" + +proc test3 {} { + global test + if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + one_line_fail_test_nochk [format { + ovsec_kadm_init admin admin "%s/a" null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + } $test] +} +if {$RPC} { test3 } + +test "init 4" + +proc test4 {} { + global test + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + one_line_fail_test_nochk [format { + ovsec_kadm_init admin admin "%s/a" null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test] +} +if {$RPC} { test4 } + +test "init 5" + +if {$RPC} { + one_line_fail_test_nochk { + ovsec_kadm_init admin admin admin null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + } +} + +test "init 6" + +proc test6 {} { + global test + + send "ovsec_kadm_init admin null \$OVSEC_KADM_ADMIN_SERVICE null \$OVSEC_KADM_STRUCT_VERSION \$OVSEC_KADM_API_VERSION_1 server_handle\n" + + expect { + {Enter password:} { } + eof { + fail "$test: eof instead of password prompt" + api_exit + api_start + return + } + timeout { + fail "$test: timeout instead of password prompt" + return + } + } + one_line_succeed_test "admin" + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if { $RPC } { test6 } + +test "init 7" +proc test7 {} { + global test + + send "ovsec_kadm_init admin \"\" \$OVSEC_KADM_ADMIN_SERVICE null \$OVSEC_KADM_STRUCT_VERSION \$OVSEC_KADM_API_VERSION_1 server_handle\n" + + expect { + {Enter password:} { } + -re "key:$" { } + eof { + fail "$test: eof instead of password prompt" + api_exit + api_start + return + } + timeout { + fail "$test: timeout instead of password prompt" + return + } + } + one_line_succeed_test "admin" + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if { $RPC } { test7 } + +test "init 8" + +proc test8 {} { + global test + if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + one_line_fail_test_nochk [format { + ovsec_kadm_init "%s/a" admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test] +} +if {$RPC} { test8 } + +test "init 9" + +if {$RPC} { + global test + one_line_fail_test_nochk { + ovsec_kadm_init admin not-the-password $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } +} + +test "init 10" + +proc test10 {} { + global test +# set prms_id 562 +# setup_xfail {*-*-*} $prms_id + one_line_fail_test_nochk { + ovsec_kadm_init null admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } +} +test10 + +#test "init 11" +# +#proc test11 {} { +# global test +# set prms_id 563 +# setup_xfail {*-*-*} $prms_id +# one_line_fail_test_nochk { +# ovsec_kadm_init "" admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# } +#} +#test11 + +test "init 12" + +proc test12 {} { + global test + one_line_fail_test_nochk [format { + ovsec_kadm_init "%s/a" admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test] +} +if {$RPC} { test12 } + +test "init 13" + +proc test13 {} { + global test + one_line_fail_test_nochk [format { + ovsec_kadm_init "%s/a@SECURE-TEST.OV.COM" admin \ + $OVSEC_KADM_ADMIN_SERVICE null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + } $test] +} +if {$RPC} { test13 } + +test "init 14" + +proc test14 {} { + global test + one_line_fail_test_nochk [format { + ovsec_kadm_init "%s/a@BAD.REALM" admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test] +} +if {$RPC} { test14 } + +test "init 15" + +if {$RPC} { + one_line_fail_test_nochk { + ovsec_kadm_init admin@BAD.REALM admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } +} + +test "init 16" + +proc test16 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test16 + +test "init 17" + +proc test17 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin@SECURE-TEST.OV.COM admin \ + $OVSEC_KADM_ADMIN_SERVICE null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test17 + +test "init 18" + +proc test18 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test18 + +test "init 19" + +proc test19 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin@SECURE-TEST.OV.COM admin \ + $OVSEC_KADM_ADMIN_SERVICE SECURE-TEST.OV.COM \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test19 + +test "init 20" + +proc test20 {} { + global test + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error_and_restart "$test: couldn't init database" + return + } + one_line_succeed_test \ + {ovsec_kadm_get_principal $server_handle admin principal} + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test20 + +#test "init 21" +# +#proc test21 {} { +# global test +# if {! [cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }]} { +# error_and_restart "$test: couldn't init database" +# return +# } +# one_line_fail_test_nochk { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# } +# if {! [cmd {ovsec_kadm_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +#} +#test21 + + +proc test22 {} { + global test prompt + set prompting 0 + send [string trim { + ovsec_kadm_init admin null null null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + }] + send "\n" + expect { + -re ":$" { set prompting 1} + -re "\nOK .*$prompt$" { fail "$test: premature success" } + -re "\nERROR .*$prompt$" { fail "$test: premature failure" } + timeout { fail "$test: timeout" } + eof { fail "$test: eof" } + } + if {$prompting} { + one_line_succeed_test mrroot + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test22 } + +test "init 22.5" +proc test225 {} { + global test prompt + set prompting 0 + send [string trim { + ovsec_kadm_init admin null null null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + }] + send "\n" + expect { + -re ":$" { set prompting 1} + -re "\nOK .*$prompt$" { fail "$test: premature success" } + -re "\nERROR .*$prompt$" { fail "$test: premature failure" } + timeout { fail "$test: timeout" } + eof { fail "$test: eof" } + } + if {$prompting} { + one_line_succeed_test mrroot + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test225 } + +test "init 23" + +proc test23 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin not-the-password $OVSEC_KADM_ADMIN_SERVICE \ + null $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test23 } + +test "init 24" + +proc test24 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin admin null null $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test24 } + +test "init 25" + +proc test25 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin admin foobar null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test25 } + +test "init 26" + +#proc test26 {} { +# global test +# +# api_exit +# api_start +# one_line_fail_test_nochk { +# ovsec_kadm_get_principal $server_handle admin principal +# } +#} +#test26 + +#test "init 27" +# +#proc test27 {} { +# global test +# +# if {! ((! [principal_exists "$test/a"]) || [delete_principal "$test/a"])} { +# error_and_restart "$test: couldn't delete principal \"$test/a\"" +# return +# } +# begin_dump +# if {[cmd [format { +# ovsec_kadm_create_principal $server_handle [simple_principal \ +# "%s/a"] {OVSEC_KADM_PRINCIPAL} "%s/a" +# } $test $test]]} { +# fail "$test: unexpected success in add" +# return +# } +# end_dump_compare "no-diffs" +#} +#test27 + +#test "init 28" +# +#proc test28 {} { +# global test prompt +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! ([cmd { +# ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ +# $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ +# server_handle +# }] && [cmd [format { +# ovsec_kadm_get_principal $server_handle "%s/a" principal +# } $test]])} { +# error_and_restart "$test: error getting principal" +# return; +# } +# send "lindex \$principal 8\n" +# expect { +# -re "\n(\[0-9\]+).*$prompt$" {set kvno $expect_out(1,string) } +# timeout { +# error_and_restart "$test: timeout getting principal kvno" +# return +# } +# eof { +# error_and_restart "$test: eof getting principal kvno" +# return +# } +# } +# api_exit +# api_start +# set new_kvno [expr "$kvno + 1"] +# if {[cmd [format { +# ovsec_kadm_modify_principal $server_handle \ +# {"%s/a" 0 0 0 0 0 0 0 %d 0 0 0} {OVSEC_KADM_KVNO} +# } $test $new_kvno]]} { +# fail "$test: unexpected success in modify" +# return; +# } +# end_dump_compare "no-diffs" +#} +#test28 + +#test "init 29" +# +#proc test29 {} { +# global test +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {[cmd [format { +# ovsec_kadm_delete_principal $server_handle "%s/a" +# } $test]]} { +# fail "$test: unexpected success in delete" +# return +# } +# end_dump_compare "no-diffs" +#} +#test29 + +test "init 30" +proc test30 {} { + global test + if {[cmd { + ovsec_kadm_init admin foobar $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error_and_restart "$test: unexpected succsess" + return + } + one_line_succeed_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if ${RPC} { test30 } + +test "init 31" +proc test31 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $bad_struct_version_mask $OVSEC_KADM_API_VERSION_1 \ + server_handle + } "BAD_STRUCT_VERSION" +} +test31 + +test "init 32" +proc test32 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $no_struct_version_mask $OVSEC_KADM_API_VERSION_1 \ + server_handle + } "BAD_STRUCT_VERSION" +} +test32 + +test "init 33" +proc test33 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $old_struct_version $OVSEC_KADM_API_VERSION_1 \ + server_handle + } "OLD_STRUCT_VERSION" +} +test33 + +test "init 34" +proc test34 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $new_struct_version $OVSEC_KADM_API_VERSION_1 \ + server_handle + } "NEW_STRUCT_VERSION" +} +test34 + +test "init 35" +proc test35 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $bad_api_version_mask \ + server_handle + } "BAD_API_VERSION" +} +test35 + +test "init 36" +proc test36 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $no_api_version_mask \ + server_handle + } "BAD_API_VERSION" +} +test36 + +test "init 37" +proc test37 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $old_api_version \ + server_handle + } "OLD_LIB_API_VERSION" +} +if { $RPC } test37 + +test "init 38" +proc test38 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $old_api_version \ + server_handle + } "OLD_SERVER_API_VERSION" +} +if { ! $RPC } test38 + +test "init 39" +proc test39 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $new_api_version \ + server_handle + } "NEW_LIB_API_VERSION" +} +if { $RPC } test39 + +test "init 40" +proc test40 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $new_api_version \ + server_handle + } "NEW_SERVER_API_VERSION" +} +if { ! $RPC } test40 + +test "init 41" +proc test41 {} { + global test + one_line_fail_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_API_VERSION_1 $OVSEC_KADM_STRUCT_VERSION \ + server_handle + } "BAD_" +} +test41 + +test "init 42" +proc test42 {} { + global test + one_line_succeed_test { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } + if {! [cmd {ovsec_kadm_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test42 + + +proc test45_46 {service} { + global test kdb5_edit env + + spawn $kdb5_edit -R "del $service" + expect { + {Type 'yes' to confirm:} { + send "yes\n" + } + default { + error "kdb5_edit del failed\n"; + } + } + expect eof + wait + + one_line_fail_test [concat {ovsec_kadm_init admin admin } \ + $service \ + { null $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle}] "SECURE_PRINC_MISSING" + + # this leaves the keytab with an incorrect entry + exec $kdb5_edit -R "ark $service" + + # restart the api so it gets a new ccache + api_exit + api_start +} + +if {$RPC} { + test "init 45" + + test45_46 ovsec_adm/admin + + test "init 46" + + test45_46 ovsec_adm/changepw + + # re-extract the keytab so it is right + exec rm /krb5/ovsec_adm.srvtab + exec $env(MAKE_KEYTAB) -princ ovsec_adm/admin -princ ovsec_adm/changepw \ + -princ kadmin/admin -princ kadmin/changepw /krb5/ovsec_adm.srvtab +} + +return "" + diff --git a/src/lib/kadm5/unit-test/api.0/mod-policy.exp b/src/lib/kadm5/unit-test/api.0/mod-policy.exp new file mode 100644 index 000000000..67f8457b6 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/mod-policy.exp @@ -0,0 +1,703 @@ +source lib.t +api_exit +api_start + +test "modify-policy 2" +proc test2 {} { + global test + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2 } + +test "modify-policy 4" +proc test4 {} { + global test + + if {! ([policy_exists "$test/a"] || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_REF_COUNT} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + +test "modify-policy 8" +proc test8 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_modify_policy $server_handle [simple_policy ""] \ + {OVSEC_KADM_PW_MAX_LIFE} + } "BAD_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +test "modify-policy 9" +proc test9 {} { + global test + global prompt + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MIN_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "modify-policy 10" +proc test10 {} { + global test + global prompt + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 32 0 0 0 0 0} \ + {OVSEC_KADM_PW_MIN_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + + +test "modify-policy 11" +proc test11 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "modify-policy 12" +proc test12 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 32 0 0 0 0} \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "modify-policy 13" +proc test13 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MIN_LENGTH} + } $test] "BAD_LENGTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "modify-policy 14" +proc test14 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 8 0 0 0} \ + {OVSEC_KADM_PW_MIN_LENGTH} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 3\n" + expect { + -re "8\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +test "modify-policy 15" +proc test15 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "modify-policy 16" +proc test16 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 0 1 0 0} \ + {OVSEC_KADM_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +test "modify-policy 17" +proc test17 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 0 5 0 0} \ + {OVSEC_KADM_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "5\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "modify-policy 18" +proc test18 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 0 6 0 0} \ + {OVSEC_KADM_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +test "modify-policy 19" +proc test19 {} { + global test + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +test "modify-policy 20" +proc test20 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 0 0 1 0} \ + {OVSEC_KADM_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +test "modify-policy 21" +proc test21 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_policy $server_handle {"%s/a" 0 0 0 0 10 0} \ + {OVSEC_KADM_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + ovsec_kadm_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "10\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +test "modify-policy 22" +proc test22 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test22 + +test "modify-policy 23" +proc test23 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test23 + +test "modify-policy 26" +proc test26 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_modify_policy $server_handle [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +test "modify-policy 30" +proc test30 {} { + global test + + one_line_fail_test [format { + ovsec_kadm_modify_policy null [simple_policy "%s/a"] \ + {OVSEC_KADM_PW_MAX_LIFE} + } $test] "BAD_SERVER_HANDLE" +} +test30 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/mod-principal.exp b/src/lib/kadm5/unit-test/api.0/mod-principal.exp new file mode 100644 index 000000000..c4bc2bed1 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/mod-principal.exp @@ -0,0 +1,1942 @@ +source lib.t +api_exit +api_start + +#test "modify-principal 1" +#proc test1 {} { +# global test +# one_line_fail_test [format { +# ovsec_kadm_modify_principal $server_handle [simple_principal \ +# "%s/a"] {OVSEC_KADM_PW_EXPIRATION} +# } $test] "NOT_INIT" +#} +#test1 + +test "modify-principal 2" +proc test2 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "AUTH_MODIFY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2 } + +test "modify-principal 4" +proc test4 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINCIPAL} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + + +test "modify-principal 5" +proc test5 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_LAST_PWD_CHANGE} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +test "modify-principal 6" +proc test6 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MOD_TIME} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test6 + +test "modify-principal 7" +proc test7 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MOD_NAME} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test7 + +test "modify-principal 8" +proc test8 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MKVNO} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +test "modify-principal 9" +proc test9 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_AUX_ATTRIBUTES} + } $test] "BAD_MASK" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "modify-principal 10" +proc test10 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "UNK_PRINC" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +test "modify-principal 11" +proc test11 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test11 } + +test "modify-principal 12" +proc test12 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test12 } + +test "modify-principal 13" +proc test13 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test13 } + +test "modify-principal 14" +proc test14 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test14 } + +test "modify-principal 15" +proc test15 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "modify-principal 17" +proc test17 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + no-policy] {OVSEC_KADM_POLICY} + } $test] "UNK_POLICY" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "modify-principal 18" +proc test18 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref + 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +test "modify-principal 19" +proc test19 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref + 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +test "modify-principal 20" +proc test20 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_POLICY_CLR} + } $test]]} { + error "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { fail "$test" } + timeout { pass "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref - 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +test "modify-principal 21" +proc test21 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol old_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol-nopw old_p2}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$old_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + send "lindex \$old_p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p2_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol new_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol-nopw new_p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$new_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + send "lindex \$new_p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p2_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$old_p1_ref - 1"] != $new_p1_ref } { + fail "$test: policy reference count is wrong" + return; + } + if { [expr "$old_p2_ref + 1"] != $new_p2_ref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +test "modify-principal 21.5" +proc test21.5 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol old_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$old_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol new_p1}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$new_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if {$old_p1_ref != $new_p1_ref} { + fail "$test: policy reference count changed ($old_p1_ref to $new_p1_ref)" + return + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21.5 + +test "modify-principal 22" +proc test22 {} { + global test + global prompt + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test22 + +test "modify-principal 23" +proc test23 {} { + global test + global prompt + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol-nopw])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test23 + +test "modify-principal 24" +proc test24 {} { + global test + global prompt +# set prms_id 1358 +# setup_xfail {*-*-*} $prms_id + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error_and_restart "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: could not modify principal" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd [format { + ovsec_kadm_get_policy $server_handle %s policy + } test-pol]]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] != $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test24 + +test "modify-principal 25" +proc test25 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test25 + +test "modify-principal 26" +proc test26 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol-nopw" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +test "modify-principal 27" +proc test27 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test27 + +test "modify-principal 28" +proc test28 {} { + global test + global prompt +# set prms_id 1358 +# setup_xfail {*-*-*} $prms_id + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 900 0 0 0 0 0 0 0 0} {OVSEC_KADM_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] == $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + pass "$test" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "modify-principal 29" +proc test29 {} { + global test + global prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { ! ([create_principal_pol "$test/a" test-pol])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_POLICY_CLR} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test29 + +test "modify-principal 30" +proc test30 {} { + global test + global prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal_pol "$test/a" test-pol])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "modify-principal 31" +proc test31 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_POLICY} + } $test]]} { + fail "modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] != $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "modify-principal 32" +proc test32 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 1234 0 0 0 0 0 0 0 0 0 0} \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 1\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "modify-principal 33" +proc test33 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_DISALLOW_ALL_TIX 0 0 0 0} \ + {OVSEC_KADM_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_DISALLOW_ALL_TIX.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test33 + +test "modify-principal 33.25" +proc test3325 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_REQUIRES_PWCHANGE 0 0 0 0} \ + {OVSEC_KADM_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_REQUIRES_PWCHANGE.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3325 + +test "modify-principal 33.5" +proc test335 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_DISALLOW_TGT_BASED 0 0 0 0} \ + {OVSEC_KADM_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_DISALLOW_TGT_BASED.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test335 + + +test "modify-principal 34" +proc test34 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 0 3456 0 0 0 0 0 0 0} {OVSEC_KADM_MAX_LIFE} + } $test]]} { + fail "$test: modify failed" + return + } + + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 4\n" + expect { + -re "3456\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test34 + +test "modify-principal 35" +proc test35 {} { + global prompt + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + ovsec_kadm_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 0 7 0 0 0} {OVSEC_KADM_KVNO} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 8\n" + expect { + -re "7\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test35 + +test "modify-principal 36" +proc test36 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol pol}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {OVSEC_KADM_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$pol 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {ovsec_kadm_get_policy $server_handle test-pol pol2}]} { + error "$test: unexpected failure on get policy" + return + } + send "lindex \$pol2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { $oldref != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test36 + +test "modify-principal 37" +proc test37 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_POLICY_CLR} + } $test]]} { + fail "$test: modify failed" + return + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test37 + +test "modify-principal 38" +proc test38 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test38 + +test "modify-principal 39" +proc test39 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [simple_principal "%s/a"] \ + {OVSEC_KADM_MAX_LIFE} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + ovsec_kadm_get_principal $server_handle "%s/a" principal + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 4\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test39 + +test "modify-principal 40" +proc test40 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_modify_principal $server_handle null \ + {OVSEC_KADM_PRINC_EXPIRE_TIME} + } "EINVAL" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test40 + +test "modify-principal 43" +proc test43 {} { + global test + one_line_fail_test [format { + ovsec_kadm_modify_principal null [simple_principal \ + "%s/a"] {OVSEC_KADM_PW_EXPIRATION} + } $test] "BAD_SERVER_HANDLE" +} +test43 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/randkey-principal.exp b/src/lib/kadm5/unit-test/api.0/randkey-principal.exp new file mode 100644 index 000000000..259cd8f03 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/randkey-principal.exp @@ -0,0 +1,319 @@ +source lib.t +api_exit +api_start + +test "randkey-principal 1" +proc test1 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + ovsec_kadm_init "%s/a" "%s/a" $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] "PASS_TOOSOON" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test1 } + +test "randkey-principal 3" +proc test3 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + ovsec_kadm_init "%s/a" "%s/a" $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] "PASS_TOOSOON" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} { test3 } + +test "randkey-principal 13" +proc test13 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + ovsec_kadm_modify_principal $server_handle [princ_w_pol "%s/a" \ + once-a-min] OVSEC_KADM_POLICY + } $test]]} { + error "$test: failed modify" + return + } + one_line_succeed_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "randkey-principal 15" +proc test15 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] "AUTH_CHANGEPW" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test15 } + +test "randkey-principal 28" +proc test28 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "randkey-principal 28.25" +proc test2825 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] "AUTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2825 } + +test "randkey-principal 28.5" +proc test285 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test285 + +test "randkey-principal 30" +proc test30 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + if {! [cmd [format { + ovsec_kadm_init "%s/a" "%s/a" $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "randkey-principal 31" +proc test31 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + ovsec_kadm_init "%s/a" "%s/a" $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_randkey_principal $server_handle "%s/a" key + } $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "randkey-principal 32" +proc test32 {} { + global test + + if { ! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + ovsec_kadm_randkey_principal $server_handle kadmin/history key + } "PROTECT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "randkey-principal 33" +proc test33 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if { ! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_randkey_principal null "%s/a" key + } $test] "BAD_SERVER_HANDLE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} + +test33 + +return "" diff --git a/src/lib/kadm5/unit-test/api.0/rename-principal.exp b/src/lib/kadm5/unit-test/api.0/rename-principal.exp new file mode 100644 index 000000000..56e412955 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.0/rename-principal.exp @@ -0,0 +1,509 @@ +source lib.t +api_exit +api_start + +#test "rename-principal 1" +#proc test1 {} { +# global test +# one_line_fail_test [format { +# ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" +# } $test $test] "NOT_INIT" +#} +#test1 + +test "rename-principal 2" +proc test2 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_CHANGEPW_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "INSUFFICIENT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + +} +if {$RPC} { test2 } + +test "rename-principal 3" +proc test3 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/none admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_INSUFFICIENT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test3 } + +test "rename-principal 4" +proc test4 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/modify admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_INSUFFICIENT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test4 } + +test "rename-principal 5" +proc test5 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_INSUFFICIENT" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test5 } + +test "rename-principal 6" +proc test6 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/mod-add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test6 } + +test "rename-principal 7" +proc test7 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/mod-delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test7 } + +test "rename-principal 8" +proc test8 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get-add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test8 } + +test "rename-principal 9" +proc test9 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/get-delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test9 } + +test "rename-principal 10" +proc test10 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/no-delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_DELETE" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test10 } + +test "rename-principal 11" +proc test11 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/no-add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH_ADD" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test11 } + +test "rename-principal 12" +proc test12 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/add admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test12 } + + +test "rename-principal 13" +proc test13 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/delete admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "AUTH" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test13 } + +test "rename-principal 14" +proc test14 {} { + global test + + if {[principal_exists "$test/a"]} { + delete_principal "$test/a" + } + + if {[create_principal_with_keysalts "$test/a" "des-cbc-crc:v4"]} { + error_and_restart "$test: couldn't create no-salt principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/rename admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +test "rename-principal 15" +proc test15 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( [principal_exists "$test/b"]) || + [create_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/rename admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "DUP" + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "rename-principal 16" +proc test16 {} { + global test + one_line_fail_test [format { + ovsec_kadm_rename_principal null "%s/a" "%s/b" + } $test $test] "BAD_SERVER_HANDLE" +} +test16 + +test "rename-principal 18" +proc test18 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! (( ! [principal_exists "$test/b"]) || + [delete_principal "$test/b"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + ovsec_kadm_init admin/rename admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + ovsec_kadm_rename_principal $server_handle "%s/a" "%s/b" + } $test $test] "NO_RENAME_SALT" + + if { ! [cmd {ovsec_kadm_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +return "" diff --git a/src/lib/kadm5/unit-test/api.1/lock.exp b/src/lib/kadm5/unit-test/api.1/lock.exp new file mode 100644 index 000000000..67ab80060 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.1/lock.exp @@ -0,0 +1,242 @@ +# This is in api.1 so that it happens after all the tests in api.0. +# If some API function does not unlock the database then the server +# (whichs runs through all api tests) will still have it locked, and +# these tests will fail. + +source lib.t + +if { $RPC } { + return +} + +set locktest $LOCKTEST +set lockfile "/krb5/ovsec_adm.lock" + +# The lock tests use the program lock-test in the unit test +# directory. The basic idea is that lock-test can be told to acquire +# various kinds of locks and then wait for input before proceeding; +# this is necessary because otherwise we'd have no way to test locking +# interactions without a race condition. +# +# lock_test_start and lock_test_continue work together to give a crude +# form of continuations. lock_test_continue expects a list of +# commands for lock-test (passed on the command line) and responses +# (read from stdout). When it gets to a command of "wait", +# lock_test_continue returns, and its return value is a list of the +# arguments that it should be passed to continue processing that +# particular list of commands for that particular lock-test after +# whatever that requried lock-test to wait has been completed. +# +# lock_test is simply a wrapper for tests that do not involve wait. + +proc lock_test_setup {test cmds} { + global locktest spawn_id + + verbose "test $test" + + set cmdline "" + foreach cmdpair $cmds { + if {[lindex $cmdpair 0] == "eof"} { + break + } + set cmdline "$cmdline [lindex $cmdpair 0]" + } + + verbose "spawning $locktest $cmdline" + eval "spawn $locktest $cmdline" +} + +proc lock_test {test cmds} { + global spawn_id + + lock_test_setup $test $cmds + set lockany [lock_test_continue $test $spawn_id 0 "" 0 $cmds] + while {$lockany != {}} { + set lockany [eval lock_test_continue $lockany] + } +} + +proc lock_test_start {test cmds} { + global spawn_id + + lock_test_setup $test $cmds + return [lock_test_continue $test $spawn_id 0 "" 0 $cmds] +} + +proc lock_test_continue {test my_spawn_id test_failed fail_output cont cmds} { + global wait_error_index wait_errno_index wait_status_index + + set spawn_id $my_spawn_id + + if {$cont == 1} { + send -i $spawn_id "\n" + } + + while {[llength $cmds] > 0} { + set cmdpair [lindex $cmds 0] + set cmds [lrange $cmds 1 end] + set cmd [lindex $cmdpair 0] + set output [lindex $cmdpair 1] + + verbose "test $test: command: $cmd" + + if {$cmd == "wait"} { + # ah, for continuations... + return [list $test $spawn_id $test_failed $fail_output 1 $cmds] + } + if {$cmd == "eof"} { + set status $output + set output "doesnotmatchanything" + } + + expect { + -i $spawn_id + -re "$output" { verbose "test $test: read: $output" } + timeout { + set test_failed 1 + set fail_output "timeout while waiting for $output" + } + eof { + if {$cmd != "eof"} { + set test_failed 1 + set fail_output "eof while waiting for $output" + } + } + } + + if {$test_failed == 1} { break } + } + + set ret [wait -i $spawn_id] + verbose "% Exit $ret" 2 + + if {$test_failed == 0} { + if {[lindex $ret $wait_error_index] == -1} { + set test_failed 1 + set fail_output "wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + verbose "test $test: status $status" + } else { + set test_failed 1 + set fail_output "unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } + } + + if {$test_failed == 0} { + pass $test + } else { + fail "$test: $fail_output" + } + + return {} +} + +lock_test 1 [list \ + [list shared "shared"] \ + [list release "released"] \ + [list eof 0]] + +lock_test 2 [list \ + [list exclusive exclusive] \ + [list release released] \ + [list eof 0]] + +lock_test 3 [list \ + [list permanent permanent] \ + [list release released] \ + [list eof 0]] + +lock_test 4 [list \ + [list release "Database not locked"] \ + [list eof 0]] + +set lock5 [lock_test_start 5 [list \ + [list shared shared] \ + [list wait ""] \ + [list eof 0]]] +lock_test 5.1 [list \ + [list shared shared] \ + [list eof 0]] +eval lock_test_continue $lock5 + +set lock6 [lock_test_start 6 [list \ + [list exclusive exclusive] \ + [list wait ""] \ + [list eof 0]]] +lock_test 6.1 [list \ + [list shared "Cannot lock database"] \ + [list eof 0]] +eval lock_test_continue $lock6 + +set lock7 [lock_test_start 7 [list \ + [list shared shared] \ + [list wait ""] \ + [list eof 0]]] +lock_test 7.1 [list \ + [list exclusive "Cannot lock database"] \ + [list eof 0]] +eval lock_test_continue $lock7 + +set lock8 [lock_test_start 8 [list \ + [list permanent permanent] \ + [list wait ""] \ + [list release "released" ] \ + [list eof 0]]] +lock_test 8.1 [list \ + [list "" "administration database lock file missing while opening database" ] \ + [list eof 1]] +eval lock_test_continue $lock8 + +set lock9 [lock_test_start 9 [list \ + [list wait ""] \ + [list exclusive "database lock file missing while getting exclusive"] \ + [list eof 0]]] +set lock9_1 [lock_test_start 9.1 [list \ + [list permanent permanent] \ + [list wait ""] \ + [list release released] \ + [list eof 0]]] +eval lock_test_continue $lock9 +eval lock_test_continue $lock9_1 + +if {! [file exists $lockfile]} { + error "lock file missing before test 10" +} +set lock10 [lock_test_start 10 [list \ + [list permanent permanent] \ + [list wait ""] \ + [list release released] \ + [list eof 0]]] +if {[file exists $lockfile]} { + fail "test 10: lock file exists" +} +eval lock_test_continue $lock10 +if {[file exists $lockfile]} { + pass "test 11: lock file exists" +} else { + fail "test 11: lock file does not exist" +} + +set lock12 [lock_test_start 12 [list \ + [list shared shared] \ + [list wait ""] \ + [list eof 0]]] +lock_test 12.1 [list \ + [list "get test-pol" retrieved] \ + [list eof 0]] +eval lock_test_continue $lock12 + +# This test doesn't work yet, somehow I or expect am confused about +# the spawn_id's between the lock-test and api. +warning "test 13: doesn't work, bug in unit-test" +# set lock13 [lock_test_start 13 [list \ +# [list "get lock13" "Principal or policy does not exist"] \ +# [list wait ""] \ +# [list "get lock13" retrieved] \ +# [list eof 0]]] +# create_policy lock13 +# eval lock_test_continue $lock13 +# delete_policy lock13 diff --git a/src/lib/kadm5/unit-test/api.2/chpass-principal-v2.exp b/src/lib/kadm5/unit-test/api.2/chpass-principal-v2.exp new file mode 100644 index 000000000..88aed8bca --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/chpass-principal-v2.exp @@ -0,0 +1,68 @@ +source lib.t +api_exit +api_start + +test "chpass-principal 200" +proc test200 {} { + global test prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + + # I'd like to specify a long list of keysalt tuples and make sure + # that chpass does the right thing, but we can only use those + # enctypes that krbtgt has a key for: des-cbc-crc:normal and + # des-cbc-crc:v4, according to the prototype kdc.conf. + if {! [cmd [format { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_chpass_principal $server_handle "%s/a" newpassword + } $test]]} { + error "$test: unexpected failure in chpass_principal" + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" p \ + {KADM5_PRINCIPAL_NORMAL_MASK KADM5_KEY_DATA} + } $test]]} { + error "$test: unexpected failure in get_principal" + } + send "lindex \$p 16\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_keys $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_keys" + return + } + eof { + error_and_restart "$test: eof getting num_keys" + return + } + } + + # XXX Perhaps I should actually check the key type returned. + if {$num_keys == 2} { + pass "$test" + } else { + fail "$test: $num_keys keys, should be 2" + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test200 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/chpass-principal.exp b/src/lib/kadm5/unit-test/api.2/chpass-principal.exp new file mode 100644 index 000000000..3efdfa9b9 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/chpass-principal.exp @@ -0,0 +1,176 @@ +source lib.t +api_exit +api_start + +test "chpass-principal 180" +proc test180 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_chpass_principal $server_handle "%s/a" FoobarBax + } $test] + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test180 } + +test "chpass-principal 180.5" +proc test1805 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_chpass_principal $server_handle "%s/a" FoobarBax + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test1805 } + +# +# admin with changepw service tickets try to change other principals +# password, failes with AUTH error +test "chpass-principal 180.625" +proc test180625 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_chpass_principal $server_handle "%s/a" password + } $test] "AUTH" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test180625 } + +test "chpass-principal 180.75" +proc test18075 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_chpass_principal $server_handle "%s/a" Foobar + } $test] "AUTH_CHANGEPW" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test18075 } + +test "chpass-principal 182" +proc test182 {} { + global test + + if { ! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_chpass_principal $server_handle kadmin/history password + } "PROTECT" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test182 + +test "chpass-principal 183" +proc test183 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if { ! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_chpass_principal null "%s/a" password + } $test] "BAD_SERVER_HANDLE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test183 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/crte-policy.exp b/src/lib/kadm5/unit-test/api.2/crte-policy.exp new file mode 100644 index 000000000..b0ea04630 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/crte-policy.exp @@ -0,0 +1,991 @@ +source lib.t +api_exit +api_start + +# Description: (1) Fails for mask with undefined bit set. +# 01/24/94: pshuang: untried. +test "create-policy 1" +proc test1 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + 0xF01000 + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test1 + +# Description: (2) Fails if caller connected with CHANGEPW_SERVICE. +test "create-policy 2" +proc test2 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy"; + return + } +} +if {$RPC} { test2 } + +# Description: (3) Fails for mask without POLICY bit set. +# 01/24/94: pshuang: untried. +test "create-policy 3" +proc test3 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + 0x000000 + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3 + +# Description: (4) Fails for mask with REF_COUNT bit set. +test "create-policy 4" +proc test4 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete policy \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_REF_COUNT} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + +# Description: (5) Fails for invalid policy name. +# 01/24/94: pshuang: untried. +test "create-policy 5" +proc test5 {} { + global test + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/"] \ + {KADM5_POLICY} + } $test] "BAD_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +# Description: (6) Fails for existing policy name. +test "create-policy 6" +proc test6 {} { + global test +# set prms_id 777 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_create_policy $server_handle [simple_policy test-pol] \ + {KADM5_POLICY} + } "DUP" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test6 + +# Description: (7) Fails for null policy name. +# 01/24/94: pshuang: untried. +test "create-policy 7" +proc test7 {} { + global test +# set prms_id 1977 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_create_policy $server_handle [simple_policy null] \ + {KADM5_POLICY} + } "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test7 + +# Description: (8) Fails for empty-string policy name. +test "create-policy 8" +proc test8 {} { + global test + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_create_policy $server_handle [simple_policy ""] \ + {KADM5_POLICY} + } "BAD_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +# Description: (9) Accepts 0 for pw_min_life. +test "create-policy 9" +proc test9 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_PW_MIN_LIFE} + } $test]]} { + fail "$test: create failed" + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +# Description: (10) Accepts non-zero for pw_min_life. +test "create-policy 10" +proc test10 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 32 0 0 0 0 0 } \ + {KADM5_POLICY KADM5_PW_MIN_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +# Description: (11) Accepts 0 for pw_max_life. +test "create-policy 11" +proc test11 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_PW_MAX_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +# Description: (12) Accepts non-zero for pw_max_life. +test "create-policy 12" +proc test12 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 32 0 0 0 0 } \ + {KADM5_POLICY KADM5_PW_MAX_LIFE} + } $test]]} { + fail "$test" + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +# Description: (13) Rejects 0 for pw_min_length. +test "create-policy 13" +proc test13 {} { + global test + global prompt + + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_PW_MIN_LENGTH} + } $test] "BAD_LENGTH" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +# Description: (14) Accepts non-zero for pw_min_length. +test "create-policy 14" +proc test14 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 8 0 0 0 } \ + {KADM5_POLICY KADM5_PW_MIN_LENGTH} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 3\n" + expect { + -re "8\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +# Description: (15) Rejects 0 for pw_min_classes. +test "create-policy 15" +proc test15 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +# Description: (16) Accepts 1 for pw_min_classes. +test "create-policy 16" +proc test16 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 1 0 0 } \ + {KADM5_POLICY KADM5_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +# Description: (17) Accepts 4 for pw_min_classes. +test "create-policy 17" +proc test17 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 5 0 0} \ + {KADM5_POLICY KADM5_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "5\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +# Description: (18) Rejects 5 for pw_min_classes. +test "create-policy 18" +proc test18 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 6 0 0} \ + {KADM5_POLICY KADM5_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +# Description: (19) Rejects 0 for pw_history_num. +test "create-policy 19" +proc test19 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY KADM5_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +# Description: (20) Accepts 1 for pw_history_num. +test "create-policy 20" +proc test20 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 0 1 0} \ + {KADM5_POLICY KADM5_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retreuve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +# Description: (21) Accepts 10 for pw_history_num. +test "create-policy 21" +proc test21 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 0 10 0} \ + {KADM5_POLICY KADM5_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "10\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +# Description: (21.5) Rejects 11 for pw_history_num. +# 01/24/94: pshuang: untried. + +test "create-policy 21.5" +proc test215 {} { + global test + global prompt + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + + one_line_fail_test [format { + kadm5_create_policy $server_handle {"%s/a" 0 0 0 0 11 0} \ + {KADM5_POLICY KADM5_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test215 + + +# Description: (22) Fails for user with no access bits. +test "create-policy 22" +proc test22 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test22 + +# Description: (23) Fails for user with "get" but not "add". +test "create-policy 23" +proc test23 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test23 + +# Description: (24) Fails for user with "modify" but not "add". +# 01/24/94: pshuang: untried. +test "create-policy 24" +proc test24 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test24 + +# Description: (25) Fails for user with "delete" but not "add". +# 01/24/94: pshuang: untried. +test "create-policy 25" +proc test25 {} { + global test + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test25 + +# Description: Succeeds for user with "add". +test "create-policy 26" +proc test26 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +# Description: Succeeds for user with "get" and "add". +# 01/24/94: pshuang: untried. +test "create-policy 27" +proc test27 {} { + global test + + if {! (( ! [policy_exists "$test/a"]) || + [delete_policy "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/get-add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_create_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test27 + +# Description: (28) Rejects null policy argument. +# 01/24/94: pshuang: untried. +test "create-policy 28" +proc test28 {} { + global test + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_create_policy $server_handle null {KADM5_POLICY} + } "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "create-policy 30" +proc test30 {} { + global test + one_line_fail_test [format { + kadm5_create_policy null [simple_policy "%s/a"] \ + {KADM5_POLICY} + } $test] "BAD_SERVER_HANDLE" +} +test30 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/crte-principal.exp b/src/lib/kadm5/unit-test/api.2/crte-principal.exp new file mode 100644 index 000000000..653d1222a --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/crte-principal.exp @@ -0,0 +1,1330 @@ +source lib.t +api_exit +api_start + +#test "create-principal 1" +# +#proc test1 {} { +# global test +# begin_dump +# one_line_fail_test [format { +# kadm5_create_principal $server_handle \ +# [simple_principal "%s/a"] {KADM5_PRINCIPAL} "%s/a" +# } $test $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test1 + +test "create-principal 2" + +proc test2 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_create_principal $server_handle null \ + {KADM5_PRINCIPAL} testpass + } "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test2 + +test "create-principal 3" +proc test3 {} { + global test +# set prms_id 777 +# setup_xfail {*-*-*} $prms_id + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} null + } $test] "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test3 + +test "create-principal 4" +proc test4 {} { + global test + + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} "" + } $test] "_Q_TOOSHORT" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test4 + +test "create-principal 5" +proc test5 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle \ + [simple_principal "%s/a"] {0x100001} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test5 + +test "create-principal 6" +proc test6 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_LAST_PWD_CHANGE} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test6 + +test "create-principal 7" +proc test7 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MOD_TIME} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test7 + +test "create-principal 8" +proc test8 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MOD_NAME} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test8 + +test "create-principal 9" +proc test9 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MKVNO} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test9 + +test "create-principal 10" +proc test10 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_AUX_ATTRIBUTES} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test10 + +test "create-principal 11" +proc test11 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_POLICY_CLR} "%s/a" + } $test $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test11 + +test "create-principal 12" +proc test12 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" + +} +if {$RPC} { test12 } + +test "create-principal 13" +proc test13 {} { + global test + begin_dump + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test13 } + +test "create-principal 14" +proc test14 {} { + global test + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test14 } + +test "create-principal 15" +proc test15 {} { + global test + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test15 } + +test "create-principal 16" +proc test16 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +if {$RPC} { test16 } + +test "create-principal 17" +proc test17 {} { + global test + + begin_dump + if {! (( [principal_exists "$test/a"]) || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "DUP" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test17 + +test "create-principal 18" +proc test18 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY} tP + } $test] "_Q_TOOSHORT" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test18 + +test "create-principal 19" +proc test19 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY} testpassword + } $test] "_Q_CLASS" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test19 + +test "create-principal 20" +proc test20 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY} Abyssinia + } $test] "_Q_DICT" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test20 + +test "create-principal 21" +proc test21 {} { + global test + + begin_dump + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" non-existant-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY} NotinTheDictionary + } $test] "UNK_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + end_dump_compare "no-diffs" +} +test21 + +test "create-principal 23" +proc test23 {} { + global test + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + one_line_succeed_test \ + [format {kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK} $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test23 + +test "create-principal 24" +proc test24 {} { + global test + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/rename admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + one_line_succeed_test \ + [format {kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK} $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test24 } + + +test "create-principal 28" +proc test28 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + + if {! [cmd [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "create-principal 29" +proc test29 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL KADM5_PRINC_EXPIRE_TIME} \ + inTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 1\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test29 + +test "create-principal 30" +proc test30 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL KADM5_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "create-principal 31" +proc test31 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol-nopw] \ + {KADM5_PRINCIPAL KADM5_POLICY \ + KADM5_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "create-principal 32" +proc test32 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + [princ_w_pol "%s/a" test-pol] \ + {KADM5_PRINCIPAL KADM5_POLICY \ + KADM5_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "create-principal 33" +proc test33 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + {"%s/a" 0 0 1234 0 null 0 0 0 0 null 0} \ + {KADM5_PRINCIPAL KADM5_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test33 + +test "create-principal 34" +proc test34 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + { "%s/a" 0 0 1234 0 null 0 0 0 0 test-pol-nopw 0} \ + {KADM5_PRINCIPAL KADM5_POLICY \ + KADM5_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test34 + +test "create-principal 35" +proc test35 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + {"%s/a" 0 0 1234 0 null 0 0 0 0 test-pol 0} \ + {KADM5_PRINCIPAL KADM5_POLICY \ + KADM5_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "1234.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test35 + +test "create-principal 36" +proc test36 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle \ + {"%s/a" 0 0 999999999 0 null 0 0 0 0 test-pol 0} \ + {KADM5_PRINCIPAL KADM5_POLICY \ + KADM5_PW_EXPIRATION} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol policy} ]} { + error_and_restart "$test: cannot retrieve policy" + return + } + + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test36 + +test "create-principal 37" +proc test37 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test37 + +test "create-principal 38" +proc test38 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {KADM5_PRINCIPAL KADM5_POLICY} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 3\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test38 + +test "create-principal 39" +proc test39 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_PRINCIPAL KADM5_POLICY} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if { ! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: cannot not retrieve principal" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 6\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$mod_date + $pw_max_life - $pw_expire"] > 5 } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test39 + +test "create-principal 40" +proc test40 {} { + global test + global prompt + + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL KADM5_PW_EXPIRATION} \ + NotinTheDictionary + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: can not retreive principal" + return; + } + send "lindex \$principal 4\n" + expect { + -re "0.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test40 + +test "create-principal 43" +proc test43 {} { + global test + one_line_fail_test [format { + kadm5_create_principal null \ + [simple_principal "%s/a"] {KADM5_PRINCIPAL} "%s/a" + } $test $test] "BAD_SERVER_HANDLE" +} +test43 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/destroy.exp b/src/lib/kadm5/unit-test/api.2/destroy.exp new file mode 100644 index 000000000..808f0b401 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/destroy.exp @@ -0,0 +1,203 @@ +source lib.t +api_exit +api_start + +test "destroy 1" + +proc test1 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_destroy $server_handle} + end_dump_compare "no-diffs" +} +test1 + +#test "destroy 2" +# +#proc test2 {} { +# global test +# begin_dump +# if {! [cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test \ +# {kadm5_get_principal $server_handle admin principal} \ +# "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test2 + +#test "destroy 3" +#proc test3 {} { +# global test +# +# begin_dump +# if {! (( ! [principal_exists "$test/a"]) || [delete_principal "$test/a"])} { +# error_and_restart "$test couldn't delete principal \"$test/a\"" +# return +# } +# if {! [cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# kadm5_create_principal $server_handle \ +# [simple_principal "%s/a"] {KADM5_PRINCIPAL} "%s/a" +# } $test $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test3 + +#test "destroy 4" +#proc test4 {} { +# global test prompt +# +# if {! (([principal_exists "$test/a"]) || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! ([cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }] && +# [cmd [format { +# kadm5_get_principal $server_handle "%s/a" principal +# } $test]])} { +# error_and_restart "$test: error getting principal" +# return; +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# kadm5_modify_principal $server_handle \ +# {"%s/a" 0 0 0 0 0 0 0 %d 0 0 0} {KADM5_KVNO} +# } $test "77"] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test4 + +#test "destroy 5" +# +#proc test5 {} { +# global test +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! [cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }]} { +# error "$test: unexpected failure on init" +# return +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# return +# } +# one_line_fail_test [format { +# kadm5_delete_principal $server_handle "%s/a" +# } $test] "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test5 + +#test "destroy 6" +# +#proc test6 {} { +# global test +# begin_dump +# one_line_fail_test {kadm5_destroy $server_handle} "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test6 + + +#test "destroy 7" +# +#proc test7 {} { +# global test +# begin_dump +# if {! [cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }]} { +# error "$test: unexpected failure in init" +# return +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +# one_line_fail_test {kadm5_destroy $server_handle} "NOT_INIT" +# end_dump_compare "no-diffs" +#} +#test7 + +test "destroy 8" +proc test8 {} { + global test + begin_dump + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } + one_line_succeed_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } + end_dump_compare "no-diffs" +} +test8 + +test "destroy 9" +proc test9 {} { + global test + one_line_fail_test {kadm5_destroy null} "BAD_SERVER_HANDLE" +} +test9 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/dlte-policy.exp b/src/lib/kadm5/unit-test/api.2/dlte-policy.exp new file mode 100644 index 000000000..95a57dc20 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/dlte-policy.exp @@ -0,0 +1,207 @@ +source lib.t +api_exit +api_start + +test "delete-policy 2" +proc test2 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {kadm5_delete_policy $server_handle ""} "BAD_POL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test2 + +test "delete-policy 5" +proc test5 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} test5 + +test "delete-policy 6" +proc test6 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} test6 + +test "delete-policy 7" +proc test7 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_policy $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test7 + +test "delete-policy 10" +proc test10 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_delete_policy $server_handle "%s/a" + } $test]]} { + fail "$test" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if { [policy_exists "$test/a"]} { + fail "$test" + return + } +} +test10 + +test "delete-policy 12" +proc test12 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test unexecpted failure in init" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [princ_w_pol "%s/a" \ + "%s/a"] {KADM5_PRINCIPAL KADM5_POLICY} \ + NotinTheDictionary + } $test $test]]} { + fail "$test: can not create principal" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {kadm5_delete_policy $server_handle test-pol} "POLICY_REF" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "delete-policy 13" +proc test13 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + one_line_fail_test [format { + kadm5_delete_policy null "%s/a" + } $test] "BAD_SERVER_HANDLE" +} +test13 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/dlte-principal.exp b/src/lib/kadm5/unit-test/api.2/dlte-principal.exp new file mode 100644 index 000000000..fe157e880 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/dlte-principal.exp @@ -0,0 +1,329 @@ +source lib.t + +api_exit +api_start + +#test "delete-principal 1" +#proc test1 {} { +# global test +# one_line_fail_test [format { +# kadm5_delete_principal $server_handle "%s/a" +# } $test] "NOT_INIT" +#} +#test1 + +test "delete-principal 2" +proc test2 {} { + global test + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {kadm5_delete_principal $server_handle null} "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: unexpected failure in destroy" + return + } +} +test2 + +test "delete-principal 5" +proc test5 {} { + global test + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "UNK_PRINC" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +test "delete-principal 6" +proc test6 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test6 } + + +test "delete-principal 7" +proc test7 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test7 } + + +test "delete-principal 8" +proc test8 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test8 } + +test "delete-principal 9" +proc test9 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test9 } + +test "delete-principal 10" +proc test10 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_delete_principal $server_handle "%s/a" + } $test] "AUTH_DELETE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test10 } + +test "delete-principal 11" +proc test11 {} { + global test + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_delete_principal $server_handle "%s/a" + } $test]]} { + fail "$test: delete failed" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if { [principal_exists "$test/a"] } { + fail "$test" + return + } +} +test11 + +test "delete-principal 12" +proc test12 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd [format { + kadm5_delete_principal $server_handle "%s/a" + } $test]]} { + fail "$test: delete failed" + return + } + if { [cmd [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + fail "$test: principal still exists" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref - 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + pass "$test" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} + +test12 + +test "delete-principal 13" +proc test13 {} { + global test + one_line_fail_test [format { + kadm5_delete_principal null "%s/a" + } $test] "BAD_SERVER_HANDLE" +} +test13 + +return "" + + + + + diff --git a/src/lib/kadm5/unit-test/api.2/get-policy.exp b/src/lib/kadm5/unit-test/api.2/get-policy.exp new file mode 100644 index 000000000..ff41c17b9 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/get-policy.exp @@ -0,0 +1,199 @@ +source lib.t +api_exit +api_start + +test "get-policy 3" +proc test3 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {kadm5_get_policy $server_handle "" p} "BAD_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3 + +test "get-policy 6" +proc test6 {} { + global test + + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {kadm5_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test6 + +test "get-policy 7" +proc test7 {} { + global test + + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {kadm5_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test7 + +test "get-policy 11" +proc test11 {} { + global test + + if {! [cmd { + kadm5_init admin/get-pol StupidAdmin $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_policy $server_handle test-pol p} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "get-policy 12" +proc test12 {} { + global test + + if {! [cmd { + kadm5_init admin/get-pol StupidAdmin \ + $KADM5_CHANGEPW_SERVICE null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {kadm5_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "get-policy 15" +proc test15 {} { + global test + + if {! [cmd { + kadm5_init admin/pol StupidAdmin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {kadm5_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "get-policy 16" +proc test16 {} { + global test + + if {! [cmd { + kadm5_init admin/pol StupidAdmin $KADM5_CHANGEPW_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {kadm5_get_policy $server_handle test-pol-nopw p} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +test "get-policy 17" +proc test17 {} { + global test + + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_policy $server_handle test-pol p} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "get-policy 18" +proc test18 {} { + global test + + if {! [cmd { + kadm5_init admin/get admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test {kadm5_get_policy $server_handle test-pol p} \ + "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } test18 + +test "get-policy 21" +proc test21 {} { + global test + + one_line_fail_test {kadm5_get_policy null "pol1" p} "BAD_SERVER_HANDLE" +} +test21 diff --git a/src/lib/kadm5/unit-test/api.2/get-principal-v2.exp b/src/lib/kadm5/unit-test/api.2/get-principal-v2.exp new file mode 100644 index 000000000..486d1a6ab --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/get-principal-v2.exp @@ -0,0 +1,234 @@ +source lib.t +api_exit +api_start + +test "get-principal 100" +proc test100 {} { + global test prompt + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd { + kadm5_get_principal $server_handle testuser p \ + {KADM5_PRINCIPAL_NORMAL_MASK} + }]} { + error "$test: unexpected failure in get_principal" + } + send "lindex \$p 16\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_keys $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_keys" + return + } + eof { + error_and_restart "$test: eof getting num_keys" + return + } + } + send "lindex \$p 17\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_tl $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_tl" + return + } + eof { + error_and_restart "$test: eof getting num_tl" + return + } + } + send "lindex \$p 18\n" + expect { + -re "({.*})\n$prompt" {set key_data $expect_out(1,string) } + -re "\n$prompt" { set key_data {} } + timeout { + error_and_restart "$test: timeout getting key_data" + return + } + eof { + error_and_restart "$test: eof getting key_data" + return + } + } + send "lindex \$p 19\n" + expect { + -re "({.*})\n$prompt" {set tl_data $expect_out(1,string) } + -re "\n$prompt" { set tl_data {} } + timeout { + error_and_restart "$test: timeout getting tl_data" + return + } + eof { + error_and_restart "$test: eof getting tl_data" + return + } + } + + set failed 0 + if {$num_keys != 0} { + fail "$test: num_keys $num_keys should be 0" + set failed 1 + } + if {$num_tl != 0} { + fail "$test: num_tl $num_tl should be 0" + set failed 1 + } + if {$key_data != {}} { + fail "$test: key_data $key_data should be {}" + set failed 1 + } + if {$tl_data != "{}"} { + fail "$test: tl_data $tl_data should be {}" + set failed 1 + } + if {$failed == 0} { + pass "$test" + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test100 + +proc test101_102 {rpc} { + global test prompt + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd { + kadm5_get_principal $server_handle testuser p \ + {KADM5_PRINCIPAL_NORMAL_MASK KADM5_KEY_DATA} + }]} { + error "$test: unexpected failure in get_principal" + } + send "lindex \$p 16\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_keys $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_keys" + return + } + eof { + error_and_restart "$test: eof getting num_keys" + return + } + } + send "lindex \$p 18\n" + expect { + -re "({.*})\n$prompt" {set key_data $expect_out(1,string) } + -re "\n$prompt" { set key_data {} } + timeout { + error_and_restart "$test: timeout getting key_data" + return + } + eof { + error_and_restart "$test: eof getting key_data" + return + } + } + + set failed 0 + if {$num_keys != 2} { + fail "$test: num_keys $num_keys should be 2" + set failed 1 + } + for {set i 0} {$i < $num_keys} {incr i} { + set key "[lindex [lindex $key_data $i] 2]" + if {($rpc && [string compare $key ""] != 0) || + ((! $rpc) && [string compare $key ""] == 0)} { + fail "$test: key_data $key is wrong" + set failed 1 + + } + } + if {$failed == 0} { pass "$test" } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test "get-principal 101" +if {$RPC} {test101_102 $RPC} +test "get-principal 102" +if {! $RPC} {test101_102 $RPC} + +test "get-principal 103" +proc test103 {} { + global test prompt + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd { + kadm5_get_principal $server_handle testuser p \ + {KADM5_PRINCIPAL_NORMAL_MASK KADM5_TL_DATA} + }]} { + error "$test: unexpected failure in get_principal" + } + send "lindex \$p 17\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_tl $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_tl" + return + } + eof { + error_and_restart "$test: eof getting num_tl" + return + } + } + send "lindex \$p 19\n" + expect { + -re "({.*})\n$prompt" {set tl_data $expect_out(1,string) } + -re "\n$prompt" { set tl_data {} } + timeout { + error_and_restart "$test: timeout getting tl_data" + return + } + eof { + error_and_restart "$test: eof getting tl_data" + return + } + } + + if {$num_tl == 0} { + fail "$test: num_tl $num_tl should not be 0" + } elseif {$tl_data == {}} { + fail "$test: tl_data $tl_data should not be {}" + } else { + pass "$test" + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test103 + +return "" + + + + diff --git a/src/lib/kadm5/unit-test/api.2/get-principal.exp b/src/lib/kadm5/unit-test/api.2/get-principal.exp new file mode 100644 index 000000000..4f91b33bb --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/get-principal.exp @@ -0,0 +1,346 @@ +source lib.t +api_exit +api_start + +test "get-principal 1" +proc test1 {} { + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test \ + {kadm5_get_principal $server_handle null p KADM5_PRINCIPAL_NORMAL_MASK} "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test1 + +test "get-principal 2" +proc test2 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "UNK_PRINC" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test2 + +test "get-principal 3" +proc test3 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test3 } + +test "get-principal 4" +proc test4 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test4 } + +test "get-principal 5" +proc test5 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test5 } + +test "get-principal 6" +proc test6 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test6 } + +test "get-principal 7" +proc test7 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/delete admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test7 } + + +test "get-principal 8" +proc test8 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/get admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_get_principal $server_handle "%s/a" p KADM5_PRINCIPAL_NORMAL_MASK + } $test] "AUTH_GET" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test8 } + + +test "get-principal 9" +proc test9 {} { + global test + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {kadm5_get_principal $server_handle admin/none p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "get-principal 10" +proc test10 {} { + global test + if {! [cmd { + kadm5_init admin/none admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test \ + {kadm5_get_principal $server_handle admin/none p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +test "get-principal 11" +proc test11 {} { + global test + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_principal $server_handle admin/get p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "get-principal 12" +proc test12 {} { + global test + if {! [cmd { + kadm5_init admin/get admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_principal $server_handle admin/get p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "get-principal 13" +proc test13 {} { + global test + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_principal $server_handle admin/add p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "get-principal 14" +proc test14 {} { + global test + if {! [cmd { + kadm5_init admin/get-mod admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test {kadm5_get_principal $server_handle admin/add p KADM5_PRINCIPAL_NORMAL_MASK} + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +test "get-principal 15" +proc test15 {} { + one_line_fail_test \ + {kadm5_get_principal null "admin" p KADM5_PRINCIPAL_NORMAL_MASK} "BAD_SERVER_HANDLE" +} +test15 + +return "" + + + + diff --git a/src/lib/kadm5/unit-test/api.2/init-v2.exp b/src/lib/kadm5/unit-test/api.2/init-v2.exp new file mode 100644 index 000000000..5de25d6fe --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/init-v2.exp @@ -0,0 +1,545 @@ +source lib.t + +api_exit +api_start + +test "init 100" +proc test100 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_PROFILE} /does-not-exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "ENOENT" +} +test100 + +test "init 101" +proc test101 {} { + global test + + # XXX Fix to work with a remote TEST_SERVER. For now, make sure + # it fails in that case. + one_line_succeed_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADMIN_SERVER KADM5_CONFIG_KADMIND_PORT} {localhost 1751}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADMIN_SERVER KADM5_CONFIG_KADMIND_PORT} {localhost 1}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "RPC_ERROR" +} +if {$RPC} test101 + +test "init 102" +proc test102 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADMIN_SERVER} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "CONFIG_BADFORMAT" +} +if {$RPC} test102 + +test "init 103" +proc test103 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_DBNAME} /does-not-exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "ENOENT" +} +if {! $RPC} test103 + +test "init 104" +proc test104 {} { + global test + + # This is slightly lame, but it works: if CONFIG_ADBNAME is obeyed, + # then the lock file will be set based on it, and it won't exist. + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADBNAME} /does-not-exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "NOLOCKFILE" +} +if {! $RPC} test104 + +test "init 105" +proc test105 {} { + global test + + # The lock info is stored in a static structure so that it applies + # to all handles in the process (why?). We need to restart the api + # in order to ensure we are using the new lockfile. + api_exit + api_start + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADB_LOCKFILE} /does-not-exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "NOLOCKFILE" + + api_exit + api_start +} +if {! $RPC} test105 + +test "init 106" +proc test106 {} { + global test prompt + + send [string trim { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MKEY_FROM_KBD} 1] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }] + send "\n" + expect { + -re ":$" { set prompting 1} + -re "\nOK .*$prompt$" { fail "$test: premature success" } + -re "\nERROR .*$prompt$" { fail "$test: premature failure" } + timeout { fail "$test: timeout" } + eof { fail "$test: eof" } + } + if {$prompting} { + one_line_succeed_test mrroot + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} test106 + +test "init 107" +proc test107 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_STASH_FILE} /does-not-exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "KDB_CANTREAD_STORED" +} +if {! $RPC} test107 + +test "init 108" +proc test108 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MKEY_NAME} does/not/exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "KDB_NOMASTERKEY" +} +if {! $RPC} test108 + +test "init 109-113" +proc test109 {} { + global test prompt + + delete_principal "$test/a" + + # I'd like to specify flags explicitly and check them, as in the + # following config_params, but tcl gets mighty confused if I do and + # I have no idea why. +# [config_params {KADM5_CONFIG_MAX_LIFE KADM5_CONFIG_MAX_RLIFE KADM5_CONFIG_EXPIRATION KADM5_CONFIG_FLAGS KADM5_CONFIG_ENCTYPES} {10 20 30 KRB5_KDB_DISALLOW_TGT_BASED {}} ] + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MAX_LIFE KADM5_CONFIG_MAX_RLIFE KADM5_CONFIG_EXPIRATION KADM5_CONFIG_ENCTYPES} {10 20 30 {}} ] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + fail "$test: cannot init with max_life" + return + } + if {! [cmd [format { + kadm5_create_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test]]} { + fail "$test: can not create principal" + return; + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" p \ + {KADM5_PRINCIPAL_NORMAL_MASK KADM5_KEY_DATA} + } $test]]} { + fail "$test: can not get principal" + return; + } + send "puts \$p\n" + send "lindex \$p 4\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting max_life" + return + } + eof { + error_and_restart "$test: eof getting max_life" + return + } + } + send "lindex \$p 12\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set max_rlife $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting max_rlife" + return + } + eof { + error_and_restart "$test: eof getting max_rlife" + return + } + } + send "lindex \$p 1\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set expiration $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting expiration" + return + } + eof { + error_and_restart "$test: eof getting expiration" + return + } + } + send "lindex \$p 7\n" + expect { + -re "(\[A-Z_\]*)\n$prompt" {set flags $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting flags" + return + } + eof { + error_and_restart "$test: eof getting flags" + return + } + } + # This sorta worries me. Since the test is setting ENCTYPES to + # nothing, the principal has no keys. That means that nothing is + # printed for the keys in the correct case; but it feels too + # likely that nothing will be printed in the case of some problem. + send "lindex \$p 18\n" + expect { + -re "({.*})\n$prompt" {set key_data $expect_out(1,string) } + -re "\n$prompt" { set key_data {} } + timeout { + error_and_restart "$test: timeout getting flags" + return + } + eof { + error_and_restart "$test: eof getting flags" + return + } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } + if {$max_life == 10} { + pass "$test" + } else { + fail "$test: $max_life is not 10" + } + if {$max_rlife == 20} { + pass "$test" + } else { + fail "$test: $max_rlife is not 20" + } + if {$expiration == 30} { + pass "$test" + } else { + fail "$test: $expiration is not 30" + } + if {$flags == ""} { + pass "$test" + } else { + fail "$test: flags $flags are wrong" + } + if {$key_data == {}} { + pass "$test" + } else { + fail "$test: key_data $key_data is wrong" + } +} +if {! $RPC} test109 + +test "init 114" +proc test114 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADMIN_SERVER} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_SERVER_PARAMS" +} +if {! $RPC} test114 + +test "init 115" +proc test115 {} { + global test + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_DBNAME} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADBNAME} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ACL_FILE} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_DICT_FILE} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ADMIN_KEYTAB} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MKEY_FROM_KBD} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_STASH_FILE} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MKEY_NAME} does.not.exist] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ENCTYPE} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MAX_LIFE} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_MAX_RLIFE} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_EXPIRATION} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_FLAGS} 0] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" + + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_ENCTYPES} {{}}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "BAD_CLIENT_PARAMS" +} +if {$RPC} test115 + +test "init 116" +proc test116 {} { + global test + + delete_principal "$test/a" + + if {! [cmd {kadm5_init admin/get-add admin $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + get_add_handle}]} { + error_and_restart "$test: couldn't init with admin/get-add" + } + + if {! [cmd {kadm5_init admin/mod-delete admin $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + mod_delete_handle}]} { + error_and_restart "$test: couldn't init with admin/get-add" + } + + one_line_succeed_test { + kadm5_get_principal $get_add_handle testuser p \ + KADM5_PRINCIPAL_NORMAL_MASK + } + one_line_succeed_test [format { + kadm5_create_principal $get_add_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] + one_line_fail_test { + kadm5_modify_principal $get_add_handle [simple_principal testuser] \ + {KADM5_PRINC_EXPIRE_TIME} + } "AUTH_MODIFY" + one_line_fail_test { + kadm5_delete_principal $get_add_handle testuser + } "AUTH_DELETE" + + one_line_fail_test { + kadm5_get_principal $mod_delete_handle testuser p \ + KADM5_PRINCIPAL_NORMAL_MASK + } "AUTH_GET" + one_line_fail_test [format { + kadm5_create_principal $mod_delete_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} testpass + } $test] "AUTH_ADD" + one_line_succeed_test { + kadm5_modify_principal $mod_delete_handle [simple_principal testuser] \ + {KADM5_PRINC_EXPIRE_TIME} + } + one_line_succeed_test [format { + kadm5_delete_principal $mod_delete_handle "%s/a" + } $test] + + if {! [cmd {kadm5_destroy $get_add_handle}]} { + error_and_restart "$test: couldn't close get_add_handle" + } + if {! [cmd {kadm5_destroy $mod_delete_handle}]} { + error_and_restart "$test: couldn't close mod_delete_handle" + } +} +if {$RPC} test116 + +send "puts \$KADM5_ADMIN_SERVICE\n" +expect { + -re "(\[a-zA-Z/@\]+)\n$prompt" { + set KADM5_ADMIN_SERVICE $expect_out(1,string) + } + default { + error_and_restart "$test: timeout/eof getting admin_service" + return + } +} + +send "puts \$KADM5_CHANGEPW_SERVICE\n" +expect { + -re "(\[a-zA-Z/@\]+)\n$prompt" { + set KADM5_CHANGEPW_SERVICE $expect_out(1,string) + } + default { + error_and_restart "$test: timeout/eof getting changepw_service" + return + } +} + +test "init 150" +proc test150 {} { + global test KADM5_ADMIN_SERVICE + + set env(KRB5CCNAME) /tmp/krb5cc_kadm5_init_v2 + kdestroy + kinit testuser notathena "-S $KADM5_ADMIN_SERVICE" + one_line_succeed_test { + kadm5_init_with_creds testuser null $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + kdestroy +} +if {$RPC} test150 + +test "init 151" +proc test151 {} { + global test KADM5_CHANGEPW_SERVICE + + set env(KRB5CCNAME) /tmp/krb5cc_kadm5_init_v2 + kdestroy + kinit testuser notathena "-S $KADM5_CHANGEPW_SERVICE" + one_line_succeed_test { + kadm5_init_with_creds testuser null $KADM5_CHANGEPW_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + kdestroy +} +if {$RPC} test151 + +test "init 152" +proc test152 {} { + global test KADM5_ADMIN_SERVICE + + kdestroy + one_line_fail_test { + kadm5_init_with_creds testuser null $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "GSS_ERROR" +} +if {$RPC} test152 + +test "init 153" +proc test153 {} { + global test KADM5_ADMIN_SERVICE + + set env(KRB5CCNAME) /tmp/krb5cc_kadm5_init_v2 + kinit testuser notathena + one_line_fail_test { + kadm5_init_with_creds testuser null $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } "GSS_ERROR" +} +if {$RPC} test153 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/init.exp b/src/lib/kadm5/unit-test/api.2/init.exp new file mode 100644 index 000000000..97a99e0ba --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/init.exp @@ -0,0 +1,731 @@ +source lib.t + +# Assumptions: +# +# Principal "admin" exists, with "get", "add", "modify" and "delete" +# access bits and password "admin". +# The string "not-the-password" isn't the password of any user in the database. +# Database master password is "mrroot". + +api_exit +api_start +test "init 1" + +one_line_fail_test_nochk \ + {kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_REALM} {""}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle} + +test "init 2" + +one_line_fail_test_nochk \ + {kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_REALM} {@}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle} + +test "init 2.5" + +one_line_fail_test_nochk \ + {kadm5_init admin admin $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_REALM} {BAD.REALM}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 server_handle} + +test "init 3" + +proc test3 {} { + global test + if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + one_line_fail_test_nochk [format { + kadm5_init admin admin "%s/a" null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + } $test] +} +if {$RPC} { test3 } + +test "init 4" + +proc test4 {} { + global test + if {! ((! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + + one_line_fail_test_nochk [format { + kadm5_init admin admin "%s/a" null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test] +} +if {$RPC} { test4 } + +test "init 5" + +if {$RPC} { + one_line_fail_test_nochk { + kadm5_init admin admin admin null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + } +} + +test "init 6" + +proc test6 {} { + global test + + send "kadm5_init admin null \$KADM5_ADMIN_SERVICE null \$KADM5_STRUCT_VERSION \$KADM5_API_VERSION_2 server_handle\n" + + expect { + {Enter password:} { } + eof { + fail "$test: eof instead of password prompt" + api_exit + api_start + return + } + timeout { + fail "$test: timeout instead of password prompt" + return + } + } + one_line_succeed_test "admin" + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if { $RPC } { test6 } + +test "init 7" +proc test7 {} { + global test + + send "kadm5_init admin \"\" \$KADM5_ADMIN_SERVICE null \$KADM5_STRUCT_VERSION \$KADM5_API_VERSION_2 server_handle\n" + + expect { + {Enter password:} { } + -re "key:$" { } + eof { + fail "$test: eof instead of password prompt" + api_exit + api_start + return + } + timeout { + fail "$test: timeout instead of password prompt" + return + } + } + one_line_succeed_test "admin" + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if { $RPC } { test7 } + +test "init 8" + +proc test8 {} { + global test + if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + one_line_fail_test_nochk [format { + kadm5_init "%s/a" admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test] +} +if {$RPC} { test8 } + +test "init 9" + +if {$RPC} { + global test + one_line_fail_test_nochk { + kadm5_init admin not-the-password $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } +} + +test "init 10" + +proc test10 {} { + global test +# set prms_id 562 +# setup_xfail {*-*-*} $prms_id + one_line_fail_test_nochk { + kadm5_init null admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } +} +test10 + +#test "init 11" +# +#proc test11 {} { +# global test +# set prms_id 563 +# setup_xfail {*-*-*} $prms_id +# one_line_fail_test_nochk { +# kadm5_init "" admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# } +#} +#test11 + +test "init 12" + +proc test12 {} { + global test + one_line_fail_test_nochk [format { + kadm5_init "%s/a" admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test] +} +if {$RPC} { test12 } + +test "init 13" + +proc test13 {} { + global test + one_line_fail_test_nochk [format { + kadm5_init "%s/a@SECURE-TEST.OV.COM" admin \ + $KADM5_ADMIN_SERVICE null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + } $test] +} +if {$RPC} { test13 } + +test "init 14" + +proc test14 {} { + global test + one_line_fail_test_nochk [format { + kadm5_init "%s/a@BAD.REALM" admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test] +} +if {$RPC} { test14 } + +test "init 15" + +if {$RPC} { + one_line_fail_test_nochk { + kadm5_init admin@BAD.REALM admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } +} + +test "init 16" + +proc test16 {} { + global test + one_line_succeed_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test16 + +test "init 17" + +proc test17 {} { + global test + one_line_succeed_test { + kadm5_init admin@SECURE-TEST.OV.COM admin \ + $KADM5_ADMIN_SERVICE null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test17 + +test "init 18" + +proc test18 {} { + global test + one_line_succeed_test { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test18 + +test "init 19" + +proc test19 {} { + global test + one_line_succeed_test { + kadm5_init admin@SECURE-TEST.OV.COM admin \ + $KADM5_ADMIN_SERVICE \ + [config_params {KADM5_CONFIG_REALM} {SECURE-TEST.OV.COM}] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test19 + +test "init 20" + +proc test20 {} { + global test + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error_and_restart "$test: couldn't init database" + return + } + one_line_succeed_test \ + {kadm5_get_principal $server_handle admin principal KADM5_PRINCIPAL_NORMAL_MASK} + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test20 + +#test "init 21" +# +#proc test21 {} { +# global test +# if {! [cmd { +# kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }]} { +# error_and_restart "$test: couldn't init database" +# return +# } +# one_line_fail_test_nochk { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +#} +#test21 + + +# proc test22 {} { +# global test prompt +# set prompting 0 +# send [string trim { +# kadm5_init admin null null null $KADM5_STRUCT_VERSION \ +# $KADM5_API_VERSION_2 server_handle +# }] +# send "\n" +# expect { +# -re ":$" { set prompting 1} +# -re "\nOK .*$prompt$" { fail "$test: premature success" } +# -re "\nERROR .*$prompt$" { fail "$test: premature failure" } +# timeout { fail "$test: timeout" } +# eof { fail "$test: eof" } +# } +# if {$prompting} { +# one_line_succeed_test mrroot +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +# } +# if {! $RPC} { test22 } +# +# test "init 22.5" +# proc test225 {} { +# global test prompt +# set prompting 0 +# send [string trim { +# kadm5_init admin null null null $KADM5_STRUCT_VERSION \ +# $KADM5_API_VERSION_2 server_handle +# }] +# send "\n" +# expect { +# -re ":$" { set prompting 1} +# -re "\nOK .*$prompt$" { fail "$test: premature success" } +# -re "\nERROR .*$prompt$" { fail "$test: premature failure" } +# timeout { fail "$test: timeout" } +# eof { fail "$test: eof" } +# } +# if {$prompting} { +# one_line_succeed_test mrroot +# } +# if {! [cmd {kadm5_destroy $server_handle}]} { +# error_and_restart "$test: couldn't close database" +# } +# } +# if {! $RPC} { test225 } + +test "init 23" + +proc test23 {} { + global test + one_line_succeed_test { + kadm5_init admin not-the-password $KADM5_ADMIN_SERVICE \ + null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test23 } + +test "init 24" + +proc test24 {} { + global test + one_line_succeed_test { + kadm5_init admin admin null null $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_2 server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test24 } + +test "init 25" + +proc test25 {} { + global test + one_line_succeed_test { + kadm5_init admin admin foobar null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if {! $RPC} { test25 } + +test "init 26" + +#proc test26 {} { +# global test +# +# api_exit +# api_start +# one_line_fail_test_nochk { +# kadm5_get_principal $server_handle admin principal +# } +#} +#test26 + +#test "init 27" +# +#proc test27 {} { +# global test +# +# if {! ((! [principal_exists "$test/a"]) || [delete_principal "$test/a"])} { +# error_and_restart "$test: couldn't delete principal \"$test/a\"" +# return +# } +# begin_dump +# if {[cmd [format { +# kadm5_create_principal $server_handle [simple_principal \ +# "%s/a"] {KADM5_PRINCIPAL} "%s/a" +# } $test $test]]} { +# fail "$test: unexpected success in add" +# return +# } +# end_dump_compare "no-diffs" +#} +#test27 + +#test "init 28" +# +#proc test28 {} { +# global test prompt +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {! ([cmd { +# kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ +# $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ +# server_handle +# }] && [cmd [format { +# kadm5_get_principal $server_handle "%s/a" principal +# } $test]])} { +# error_and_restart "$test: error getting principal" +# return; +# } +# send "lindex \$principal 8\n" +# expect { +# -re "\n(\[0-9\]+).*$prompt$" {set kvno $expect_out(1,string) } +# timeout { +# error_and_restart "$test: timeout getting principal kvno" +# return +# } +# eof { +# error_and_restart "$test: eof getting principal kvno" +# return +# } +# } +# api_exit +# api_start +# set new_kvno [expr "$kvno + 1"] +# if {[cmd [format { +# kadm5_modify_principal $server_handle \ +# {"%s/a" 0 0 0 0 0 0 0 %d 0 0 0} {KADM5_KVNO} +# } $test $new_kvno]]} { +# fail "$test: unexpected success in modify" +# return; +# } +# end_dump_compare "no-diffs" +#} +#test28 + +#test "init 29" +# +#proc test29 {} { +# global test +# +# if {! ([principal_exists "$test/a"] || [create_principal "$test/a"])} { +# error_and_restart "$test: couldn't create principal \"$test/a\"" +# return +# } +# begin_dump +# if {[cmd [format { +# kadm5_delete_principal $server_handle "%s/a" +# } $test]]} { +# fail "$test: unexpected success in delete" +# return +# } +# end_dump_compare "no-diffs" +#} +#test29 + +test "init 30" +proc test30 {} { + global test + if {[cmd { + kadm5_init admin foobar $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error_and_restart "$test: unexpected succsess" + return + } + one_line_succeed_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +if ${RPC} { test30 } + +test "init 31" +proc test31 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $bad_struct_version_mask $KADM5_API_VERSION_2 \ + server_handle + } "BAD_STRUCT_VERSION" +} +test31 + +test "init 32" +proc test32 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $no_struct_version_mask $KADM5_API_VERSION_2 \ + server_handle + } "BAD_STRUCT_VERSION" +} +test32 + +test "init 33" +proc test33 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $old_struct_version $KADM5_API_VERSION_2 \ + server_handle + } "OLD_STRUCT_VERSION" +} +test33 + +test "init 34" +proc test34 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $new_struct_version $KADM5_API_VERSION_2 \ + server_handle + } "NEW_STRUCT_VERSION" +} +test34 + +test "init 35" +proc test35 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $bad_api_version_mask \ + server_handle + } "BAD_API_VERSION" +} +test35 + +test "init 36" +proc test36 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $no_api_version_mask \ + server_handle + } "BAD_API_VERSION" +} +test36 + +test "init 37" +proc test37 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $old_api_version \ + server_handle + } "OLD_LIB_API_VERSION" +} +if { $RPC } test37 + +test "init 38" +proc test38 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $old_api_version \ + server_handle + } "OLD_SERVER_API_VERSION" +} +if { ! $RPC } test38 + +test "init 39" +proc test39 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $new_api_version \ + server_handle + } "NEW_LIB_API_VERSION" +} +if { $RPC } test39 + +test "init 40" +proc test40 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $new_api_version \ + server_handle + } "NEW_SERVER_API_VERSION" +} +if { ! $RPC } test40 + +test "init 41" +proc test41 {} { + global test + one_line_fail_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_API_VERSION_2 $KADM5_STRUCT_VERSION \ + server_handle + } "BAD_" +} +test41 + +test "init 42" +proc test42 {} { + global test + one_line_succeed_test { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } + if {! [cmd {kadm5_destroy $server_handle}]} { + error_and_restart "$test: couldn't close database" + } +} +test42 + + +proc test45_46 {service} { + global test kdb5_edit env + + spawn $kdb5_edit -R "del $service" + expect { + {Type 'yes' to confirm:} { + send "yes\n" + } + default { + error "kdb5_edit del failed\n"; + } + } + expect eof + wait + + one_line_fail_test [concat {kadm5_init admin admin } \ + $service \ + { null $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle}] "SECURE_PRINC_MISSING" + + # this leaves the keytab with an incorrect entry + exec $kdb5_edit -R "ark $service" + + # restart the api so it gets a new ccache + api_exit + api_start +} + +if {$RPC} { + test "init 45" + + test45_46 ovsec_adm/admin + + test "init 46" + + test45_46 ovsec_adm/changepw + + # re-extract the keytab so it is right + exec rm /krb5/ovsec_adm.srvtab + exec $env(MAKE_KEYTAB) -princ ovsec_adm/admin -princ ovsec_adm/changepw \ + -princ kadmin/admin -princ kadmin/changepw /krb5/ovsec_adm.srvtab +} + +return "" + diff --git a/src/lib/kadm5/unit-test/api.2/mod-policy.exp b/src/lib/kadm5/unit-test/api.2/mod-policy.exp new file mode 100644 index 000000000..07e2e62db --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/mod-policy.exp @@ -0,0 +1,703 @@ +source lib.t +api_exit +api_start + +test "modify-policy 2" +proc test2 {} { + global test + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2 } + +test "modify-policy 4" +proc test4 {} { + global test + + if {! ([policy_exists "$test/a"] || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_REF_COUNT} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + +test "modify-policy 8" +proc test8 {} { + global test +# set prms_id 744 +# setup_xfail {*-*-*} $prms_id + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_modify_policy $server_handle [simple_policy ""] \ + {KADM5_PW_MAX_LIFE} + } "BAD_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +test "modify-policy 9" +proc test9 {} { + global test + global prompt + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MIN_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "modify-policy 10" +proc test10 {} { + global test + global prompt + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 32 0 0 0 0 0} \ + {KADM5_PW_MIN_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 1\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + + +test "modify-policy 11" +proc test11 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test11 + +test "modify-policy 12" +proc test12 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 32 0 0 0 0} \ + {KADM5_PW_MAX_LIFE} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 2\n" + expect { + -re "32\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test12 + +test "modify-policy 13" +proc test13 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MIN_LENGTH} + } $test] "BAD_LENGTH" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "modify-policy 14" +proc test14 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 8 0 0 0} \ + {KADM5_PW_MIN_LENGTH} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 3\n" + expect { + -re "8\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test14 + +test "modify-policy 15" +proc test15 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "modify-policy 16" +proc test16 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 0 1 0 0} \ + {KADM5_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test16 + +test "modify-policy 17" +proc test17 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a"])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 0 5 0 0} \ + {KADM5_PW_MIN_CLASSES} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 4\n" + expect { + -re "5\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "modify-policy 18" +proc test18 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 0 6 0 0} \ + {KADM5_PW_MIN_CLASSES} + } $test] "BAD_CLASS" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +test "modify-policy 19" +proc test19 {} { + global test + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_HISTORY_NUM} + } $test] "BAD_HISTORY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +test "modify-policy 20" +proc test20 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 0 0 1 0} \ + {KADM5_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "1\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +test "modify-policy 21" +proc test21 {} { + global test + global prompt + + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_policy $server_handle {"%s/a" 0 0 0 0 10 0} \ + {KADM5_PW_HISTORY_NUM} + } $test]]} { + fail $test + return + } + if {! [cmd [format { + kadm5_get_policy $server_handle "%s/a" policy + } $test]]} { + fail "$test: can not retrieve policy" + return + } + send "lindex \$policy 5\n" + expect { + -re "10\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +test "modify-policy 22" +proc test22 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test22 + +test "modify-policy 23" +proc test23 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test] "AUTH_MODIFY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} test23 + +test "modify-policy 26" +proc test26 {} { + global test + if {! (( [policy_exists "$test/a"]) || + [create_policy "$test/a" ])} { + error_and_restart "$test: couldn't create policy \"$test/a\"" + return + } + + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_modify_policy $server_handle [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +test "modify-policy 30" +proc test30 {} { + global test + + one_line_fail_test [format { + kadm5_modify_policy null [simple_policy "%s/a"] \ + {KADM5_PW_MAX_LIFE} + } $test] "BAD_SERVER_HANDLE" +} +test30 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/mod-principal.exp b/src/lib/kadm5/unit-test/api.2/mod-principal.exp new file mode 100644 index 000000000..5e24e08b0 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/mod-principal.exp @@ -0,0 +1,1942 @@ +source lib.t +api_exit +api_start + +#test "modify-principal 1" +#proc test1 {} { +# global test +# one_line_fail_test [format { +# kadm5_modify_principal $server_handle [simple_principal \ +# "%s/a"] {KADM5_PW_EXPIRATION} +# } $test] "NOT_INIT" +#} +#test1 + +test "modify-principal 2" +proc test2 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "AUTH_MODIFY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2 } + +test "modify-principal 4" +proc test4 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINCIPAL} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test4 + + +test "modify-principal 5" +proc test5 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_LAST_PWD_CHANGE} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test5 + +test "modify-principal 6" +proc test6 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MOD_TIME} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test6 + +test "modify-principal 7" +proc test7 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MOD_NAME} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test7 + +test "modify-principal 8" +proc test8 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MKVNO} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test8 + +test "modify-principal 9" +proc test9 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_AUX_ATTRIBUTES} + } $test] "BAD_MASK" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test9 + +test "modify-principal 10" +proc test10 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "UNK_PRINC" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test10 + +test "modify-principal 11" +proc test11 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/none admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test11 } + +test "modify-principal 12" +proc test12 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/get admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test12 } + +test "modify-principal 13" +proc test13 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/add admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test13 } + +test "modify-principal 14" +proc test14 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/delete admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] "AUTH_MOD" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test14 } + +test "modify-principal 15" +proc test15 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test15 + +test "modify-principal 17" +proc test17 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + no-policy] {KADM5_POLICY} + } $test] "UNK_POLICY" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test17 + +test "modify-principal 18" +proc test18 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref + 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test18 + +test "modify-principal 19" +proc test19 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref + 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test19 + +test "modify-principal 20" +proc test20 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_POLICY_CLR} + } $test]]} { + error "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { fail "$test" } + timeout { pass "$test" } + } + send "lindex \$p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$oldref - 1"] != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test20 + +test "modify-principal 21" +proc test21 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol old_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol-nopw old_p2}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$old_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + send "lindex \$old_p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p2_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if { ! [cmd {kadm5_get_policy $server_handle test-pol new_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol-nopw new_p2}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$new_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + send "lindex \$new_p2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p2_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { [expr "$old_p1_ref - 1"] != $new_p1_ref } { + fail "$test: policy reference count is wrong" + return; + } + if { [expr "$old_p2_ref + 1"] != $new_p2_ref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21 + +test "modify-principal 21.5" +proc test21.5 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol old_p1}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$old_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set old_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if { ! [cmd {kadm5_get_policy $server_handle test-pol new_p1}]} { + error "$test: unexpected failure on get policy" + return + } + + send "lindex \$new_p1 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set new_p1_ref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + + if {$old_p1_ref != $new_p1_ref} { + fail "$test: policy reference count changed ($old_p1_ref to $new_p1_ref)" + return + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test21.5 + +test "modify-principal 22" +proc test22 {} { + global test + global prompt + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test22 + +test "modify-principal 23" +proc test23 {} { + global test + global prompt + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" test-pol-nopw])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test23 + +test "modify-principal 24" +proc test24 {} { + global test + global prompt +# set prms_id 1358 +# setup_xfail {*-*-*} $prms_id + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error_and_restart "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: could not modify principal" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd [format { + kadm5_get_policy $server_handle %s policy + } test-pol]]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] != $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test24 + +test "modify-principal 25" +proc test25 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test25 + +test "modify-principal 26" +proc test26 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol-nopw" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test26 + +test "modify-principal 27" +proc test27 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 1234 0 0 0 0 0 0 0 0} {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test27 + +test "modify-principal 28" +proc test28 {} { + global test + global prompt +# set prms_id 1358 +# setup_xfail {*-*-*} $prms_id + + if {! (( [principal_exists "$test/a"]) || + [create_principal_pol "$test/a" "test-pol" ])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 900 0 0 0 0 0 0 0 0} {KADM5_PW_EXPIRATION} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] == $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + pass "$test" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "modify-principal 29" +proc test29 {} { + global test + global prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { ! ([create_principal_pol "$test/a" test-pol])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_POLICY_CLR} + } $test]]} { + fail "$test: modifiy failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test29 + +test "modify-principal 30" +proc test30 {} { + global test + global prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal_pol "$test/a" test-pol])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol-nopw] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 3\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "modify-principal 31" +proc test31 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_POLICY} + } $test]]} { + fail "modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol policy}]} { + error_and_restart "$test: cannot retrieve policy" + return + } + send "lindex \$principal 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_mod_date $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_mod_date" + return + } + eof { + error_and_restart "$test: eof getting pw_mod_date" + return + } + } + + send "lindex \$principal 3\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_expire $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_expire" + return + } + eof { + error_and_restart "$test: eof getting pw_expire" + return + } + } + + send "lindex \$policy 2\n" + expect { + -re "(\[0-9\]+)\n$prompt" {set pw_max_life $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting pw_max_life" + return + } + eof { + error_and_restart "$test: eof getting pw_max_life" + return + } + } + if { [expr "$pw_mod_date + $pw_max_life"] != $pw_expire } { + fail "$test: pw_expire is wrong" + return + } + + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "modify-principal 32" +proc test32 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 1234 0 0 0 0 0 0 0 0 0 0} \ + {KADM5_PRINC_EXPIRE_TIME} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 1\n" + expect { + -re "1234\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "modify-principal 33" +proc test33 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_DISALLOW_ALL_TIX 0 0 0 0} \ + {KADM5_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_DISALLOW_ALL_TIX.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test33 + +test "modify-principal 33.25" +proc test3325 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_REQUIRES_PWCHANGE 0 0 0 0} \ + {KADM5_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_REQUIRES_PWCHANGE.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test3325 + +test "modify-principal 33.5" +proc test335 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 KRB5_KDB_DISALLOW_TGT_BASED 0 0 0 0} \ + {KADM5_ATTRIBUTES} + } $test]]} { + fail "$test: modified fail" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 7\n" + expect { + -re "KRB5_KDB_DISALLOW_TGT_BASED.*$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test335 + + +test "modify-principal 34" +proc test34 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 0 3456 0 0 0 0 0 0 0} {KADM5_MAX_LIFE} + } $test]]} { + fail "$test: modify failed" + return + } + + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 4\n" + expect { + -re "3456\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test34 + +test "modify-principal 35" +proc test35 {} { + global prompt + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd [format { + kadm5_modify_principal $server_handle \ + {"%s/a" 0 0 0 0 0 0 0 7 0 0 0} {KADM5_KVNO} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 8\n" + expect { + -re "7\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test35 + +test "modify-principal 36" +proc test36 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal_pol "$test/a" "test-pol"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol pol}]} { + error "$test: unexpected failure on get policy" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + test-pol] {KADM5_POLICY} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 10\n" + expect { + -re "test-pol\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + send "lindex \$pol 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set oldref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { ! [cmd {kadm5_get_policy $server_handle test-pol pol2}]} { + error "$test: unexpected failure on get policy" + return + } + send "lindex \$pol2 6\n" + expect { + -re "(\[0-9\]+)\n$prompt$" {set newref $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting principal kvno (second time)" + return + } + eof { + error_and_restart "$test: eof getting principal kvno (second time)" + return + } + } + if { $oldref != $newref } { + fail "$test: policy reference count is wrong" + return; + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test36 + +test "modify-principal 37" +proc test37 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if { !( [create_principal "$test/a"])} { + error_and_restart "$test: could not create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_POLICY_CLR} + } $test]]} { + fail "$test: modify failed" + return + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test37 + +test "modify-principal 38" +proc test38 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_PRINC_EXPIRE_TIME} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 1\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test38 + +test "modify-principal 39" +proc test39 {} { + global test + global prompt + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! ([create_principal "$test/a"])} { + error "$test: unexpected failure in creating principal" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [simple_principal "%s/a"] \ + {KADM5_MAX_LIFE} + } $test]]} { + fail "$test: modify failed" + return + } + if {! [cmd [format { + kadm5_get_principal $server_handle "%s/a" principal KADM5_PRINCIPAL_NORMAL_MASK + } $test]]} { + error_and_restart "$test: could not retrieve principal" + return + } + send "lindex \$principal 4\n" + expect { + -re "0\n$prompt$" { pass "$test" } + timeout { fail "$test" } + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test39 + +test "modify-principal 40" +proc test40 {} { + global test + global prompt + + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_modify_principal $server_handle null \ + {KADM5_PRINC_EXPIRE_TIME} + } "EINVAL" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test40 + +test "modify-principal 43" +proc test43 {} { + global test + one_line_fail_test [format { + kadm5_modify_principal null [simple_principal \ + "%s/a"] {KADM5_PW_EXPIRATION} + } $test] "BAD_SERVER_HANDLE" +} +test43 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/randkey-principal-v2.exp b/src/lib/kadm5/unit-test/api.2/randkey-principal-v2.exp new file mode 100644 index 000000000..c3dfd18df --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/randkey-principal-v2.exp @@ -0,0 +1,62 @@ +source lib.t +api_exit +api_start + +test "randkey-principal 100" +proc test100 {} { + global test prompt + + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + + # I'd like to specify a long list of keysalt tuples and make sure + # that randkey does the right thing, but we can only use those + # enctypes that krbtgt has a key for: des-cbc-crc:normal and + # des-cbc-crc:v4, according to the prototype kdc.conf. + if {! [cmd [format { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test]]} { + error "$test: unexpected failure in randkey_principal" + } + send "puts \$num_keys\n" + expect { + -re "(\[0-9\]+)\n$prompt" { set num_keys $expect_out(1,string) } + timeout { + error_and_restart "$test: timeout getting num_keys" + return + } + eof { + error_and_restart "$test: eof getting num_keys" + return + } + } + + # XXX Perhaps I should actually check the key type returned. + if {$num_keys == 1} { + pass "$test" + } else { + fail "$test: $num_keys keys, should be 1" + } + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test100 + +return "" diff --git a/src/lib/kadm5/unit-test/api.2/randkey-principal.exp b/src/lib/kadm5/unit-test/api.2/randkey-principal.exp new file mode 100644 index 000000000..d693a2ac1 --- /dev/null +++ b/src/lib/kadm5/unit-test/api.2/randkey-principal.exp @@ -0,0 +1,319 @@ +source lib.t +api_exit +api_start + +test "randkey-principal 1" +proc test1 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + kadm5_init "%s/a" "%s/a" $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] "PASS_TOOSOON" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test1 } + +test "randkey-principal 3" +proc test3 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + kadm5_init "%s/a" "%s/a" $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] "PASS_TOOSOON" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if ${RPC} { test3 } + +test "randkey-principal 13" +proc test13 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + if {! [cmd [format { + kadm5_modify_principal $server_handle [princ_w_pol "%s/a" \ + once-a-min] KADM5_POLICY + } $test]]} { + error "$test: failed modify" + return + } + one_line_succeed_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test13 + +test "randkey-principal 15" +proc test15 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal_pol "$test/a" once-a-min]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] "AUTH_CHANGEPW" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if { $RPC } { test15 } + +test "randkey-principal 28" +proc test28 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test28 + +test "randkey-principal 28.25" +proc test2825 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin admin $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] "AUTH" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +if {$RPC} { test2825 } + +test "randkey-principal 28.5" +proc test285 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [cmd { + kadm5_init admin/modify admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test285 + +test "randkey-principal 30" +proc test30 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't delete principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + if {! [cmd [format { + kadm5_init "%s/a" "%s/a" $KADM5_CHANGEPW_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test30 + +test "randkey-principal 31" +proc test31 {} { + global test + if {! (( ! [principal_exists "$test/a"]) || + [delete_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if {! [create_principal "$test/a"]} { + error_and_restart "$test: creating principal" + return + } + + if {! [cmd [format { + kadm5_init "%s/a" "%s/a" $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + } $test $test]]} { + error "$test: unexpected failure in init" + return + } + one_line_succeed_test [format { + kadm5_randkey_principal $server_handle "%s/a" keys num_keys + } $test] + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test31 + +test "randkey-principal 32" +proc test32 {} { + global test + + if { ! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test { + kadm5_randkey_principal $server_handle kadmin/history keys num_keys + } "PROTECT" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} +test32 + +test "randkey-principal 33" +proc test33 {} { + global test + if {! (( [principal_exists "$test/a"]) || + [create_principal "$test/a"])} { + error_and_restart "$test: couldn't create principal \"$test/a\"" + return + } + if { ! [cmd { + kadm5_init admin admin $KADM5_ADMIN_SERVICE null \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ + server_handle + }]} { + error "$test: unexpected failure in init" + return + } + one_line_fail_test [format { + kadm5_randkey_principal null "%s/a" keys num_keys + } $test] "BAD_SERVER_HANDLE" + if { ! [cmd {kadm5_destroy $server_handle}]} { + error "$test: unexpected failure in destroy" + return + } +} + +test33 + +return "" diff --git a/src/lib/kadm5/unit-test/config/unix.exp b/src/lib/kadm5/unit-test/config/unix.exp new file mode 100644 index 000000000..e9a681f93 --- /dev/null +++ b/src/lib/kadm5/unit-test/config/unix.exp @@ -0,0 +1,113 @@ +set stty_init {-onlcr -opost intr \^C kill \^U} +set kdb5_edit $KDBFIVE_EDIT +set kadmin_local $KADMIN_LOCAL + +# Backward compatibility until we're using expect 5 everywhere +if {[info exists exp_version_4]} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc api_exit {} { + global spawn_id + +# puts stdout "Starting api_exit (spawn_id $spawn_id)." + catch {close} errMsg + catch {wait} errMsg +# puts stdout "Finishing api_exit." +} + +proc api_version {} { +} + +proc api_start {} { + global API + global env + global spawn_id + global prompt + + spawn $API + expect { + -re "$prompt$" {} + eof { error "EOF starting API" } + timeout { error "Timeout starting API" } + } + if {! [info exists env(TCLUTIL)]} { + error "TCLUTIL environment variable isn't set" + } + send "source $env(TCLUTIL)\n" + expect { + -re "$prompt$" {} + eof { error "EOF starting API" } + timeout { error "Timeout starting API" } + } + send "set current_struct_version \[expr \$OVSEC_KADM_STRUCT_VERSION &~ \$OVSEC_KADM_STRUCT_VERSION_MASK\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set current_api_version \[expr \$OVSEC_KADM_API_VERSION_1 &~ \$OVSEC_KADM_API_VERSION_MASK\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set bad_struct_version_mask \[expr 0x65432100 | \$current_struct_version\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set bad_api_version_mask \[expr 0x65432100 | \$current_api_version\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set no_api_version_mask \$current_api_version\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set no_struct_version_mask \$current_struct_version\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set old_api_version \[expr \$OVSEC_KADM_API_VERSION_MASK | 0x00\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set old_struct_version \[expr \$OVSEC_KADM_STRUCT_VERSION_MASK | 0x00\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set new_api_version \[expr \$OVSEC_KADM_API_VERSION_MASK | 0xca\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } + send "set new_struct_version \[expr \$OVSEC_KADM_STRUCT_VERSION_MASK | 0xca\]\n" + expect { + -re "$prompt$" {} + eof { error "EOF setting API varibles"} + timeout { error "timeout setting API varibles"} + } +# puts stdout "Finishing api_start (spawn_id $spawn_id)." +} +api_start + diff --git a/src/lib/kadm5/unit-test/destroy-test.c b/src/lib/kadm5/unit-test/destroy-test.c new file mode 100644 index 000000000..0db69c3a1 --- /dev/null +++ b/src/lib/kadm5/unit-test/destroy-test.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_NUM 25 + +main() +{ + ovsec_kadm_ret_t ret; + char *cp; + int x, i; + void *server_handle; + kadm5_server_handle_t handle; + + for(x = 0; x < TEST_NUM; x++) { + ret = ovsec_kadm_init("admin", "admin", "ovsec_adm/admin", 0, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if(ret != OVSEC_KADM_OK) { + com_err("test", ret, "init"); + exit(2); + } + handle = (kadm5_server_handle_t) server_handle; + cp = (char *) strdup(((char *) (strchr(handle->cache_name, ':')) + 1)); + ovsec_kadm_destroy(server_handle); + if(access(cp, F_OK) == 0) { + puts("ticket cache not destroyed"); + exit(2); + } + free(cp); + } + exit(0); +} + diff --git a/src/lib/kadm5/unit-test/diff-files/destroy-1 b/src/lib/kadm5/unit-test/diff-files/destroy-1 new file mode 100644 index 000000000..593d67320 --- /dev/null +++ b/src/lib/kadm5/unit-test/diff-files/destroy-1 @@ -0,0 +1,2 @@ +##! nochanges + diff --git a/src/lib/kadm5/unit-test/diff-files/no-diffs b/src/lib/kadm5/unit-test/diff-files/no-diffs new file mode 100644 index 000000000..593d67320 --- /dev/null +++ b/src/lib/kadm5/unit-test/diff-files/no-diffs @@ -0,0 +1,2 @@ +##! nochanges + diff --git a/src/lib/kadm5/unit-test/handle-test.c b/src/lib/kadm5/unit-test/handle-test.c new file mode 100644 index 000000000..ced1d183d --- /dev/null +++ b/src/lib/kadm5/unit-test/handle-test.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +main(int argc, char *argv[]) +{ + ovsec_kadm_ret_t ret; + void *server_handle; + kadm5_server_handle_t handle; + kadm5_server_handle_rec orig_handle; + ovsec_kadm_policy_ent_t pol; + ovsec_kadm_principal_ent_t princ; + krb5_keyblock *key; + krb5_principal tprinc; + krb5_context context; + int *p; + + + krb5_init_context(&context); + + ret = ovsec_kadm_init("admin/none", "admin", "ovsec_adm/admin", 0, + OVSEC_KADM_STRUCT_VERSION, OVSEC_KADM_API_VERSION_1, + &server_handle); + if(ret != OVSEC_KADM_OK) { + com_err("test", ret, "init"); + exit(2); + } + handle = (kadm5_server_handle_t) server_handle; + orig_handle = *handle; + handle->magic_number = OVSEC_KADM_STRUCT_VERSION; + krb5_parse_name(context, "testuser", &tprinc); + ret = ovsec_kadm_get_principal(server_handle, tprinc, &princ); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "get-principal", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_get_policy(server_handle, "pol1", &pol); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "get-policy", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_create_principal(server_handle, princ, OVSEC_KADM_PRINCIPAL, "pass"); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "create-principal", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_create_policy(server_handle, pol, OVSEC_KADM_POLICY); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "create-policy", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_modify_principal(server_handle, princ, OVSEC_KADM_PW_EXPIRATION); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "modify-principal", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_modify_policy(server_handle, pol, OVSEC_KADM_PW_MAX_LIFE); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "modify-policy", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_delete_principal(server_handle, tprinc); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "delete-principal", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_delete_policy(server_handle, "pol1"); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "delete-policy", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_chpass_principal(server_handle, tprinc, "FooBar"); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "chpass", + error_message(ret)); + exit(1); + } + ret = ovsec_kadm_randkey_principal(server_handle, tprinc, &key); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "randkey", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_rename_principal(server_handle, tprinc, tprinc); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "rename", + error_message(ret)); + exit(1); + } + + ret = ovsec_kadm_destroy(server_handle); + if(ret != OVSEC_KADM_BAD_SERVER_HANDLE) { + fprintf(stderr, "%s -- returned -- %s\n", "destroy", + error_message(ret)); + exit(1); + } + + *handle = orig_handle; + ret = ovsec_kadm_destroy(server_handle); + if (ret != OVSEC_KADM_OK) { + fprintf(stderr, "valid %s -- returned -- %s\n", "destroy", + error_message(ret)); + exit(1); + } + + exit(0); +} diff --git a/src/lib/kadm5/unit-test/init-test.c b/src/lib/kadm5/unit-test/init-test.c new file mode 100644 index 000000000..823dd222b --- /dev/null +++ b/src/lib/kadm5/unit-test/init-test.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +main() +{ + ovsec_kadm_ret_t ret; + void *server_handle; + + ret = ovsec_kadm_init("admin", "admin", OVSEC_KADM_ADMIN_SERVICE, 0, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if (ret == OVSEC_KADM_RPC_ERROR) + exit(0); + else if (ret != OVSEC_KADM_OK) { + com_err("init-test", ret, "while (hacked) initializing"); + exit(1); + } + else { + fprintf(stderr, "Unexpected success while (hacked) initializing!\n"); + (void) ovsec_kadm_destroy(server_handle); + exit(1); + } +} diff --git a/src/lib/kadm5/unit-test/iter-test.c b/src/lib/kadm5/unit-test/iter-test.c new file mode 100644 index 000000000..7ca43d44f --- /dev/null +++ b/src/lib/kadm5/unit-test/iter-test.c @@ -0,0 +1,47 @@ +#include +#include + +main(int argc, char **argv) +{ + ovsec_kadm_ret_t ret; + void *server_handle; + char **names; + int count, princ; + + if (argc != 3) { + fprintf(stderr, "Usage: %s [-princ|-pol] exp\n", argv[0]); + exit(1); + } + princ = (strcmp(argv[1], "-princ") == 0); + + ret = ovsec_kadm_init("admin", "admin", OVSEC_KADM_ADMIN_SERVICE, 0, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if (ret != OVSEC_KADM_OK) { + com_err("iter-test", ret, "while initializing"); + exit(1); + } + + if (princ) + ret = ovsec_kadm_get_principals(server_handle, argv[2], &names, + &count); + else + ret = ovsec_kadm_get_policies(server_handle, argv[2], + &names, &count); + + if (ret != OVSEC_KADM_OK) { + com_err("iter-test", ret, "while retrieving list"); + exit(1); + } + + for (ret = 0; ret < count; ret++) + printf("%d: %s\n", ret, names[ret]); + + ovsec_kadm_free_name_list(server_handle, names, count); + + (void) ovsec_kadm_destroy(server_handle); + + return 0; +} + diff --git a/src/lib/kadm5/unit-test/lib.t b/src/lib/kadm5/unit-test/lib.t new file mode 100644 index 000000000..110514de8 --- /dev/null +++ b/src/lib/kadm5/unit-test/lib.t @@ -0,0 +1,367 @@ +global timeout +set timeout 60 + +proc cmd {command} { + global prompt + global spawn_id + global test + + send "[string trim $command]\n" + expect { + -re "OK .*$prompt$" { return 1 } + -re "ERROR .*$prompt$" { return 0 } + "wrong # args" { error "$test: wrong number args"; return 0 } + timeout { fail "$test: timeout"; return 0 } + eof { fail "$test: eof"; api_exit; api_start; return 0 } + } +} + +proc tcl_cmd {command} { + global prompt + global spawn_id + + send "[string trim $command]\n" + expect { + -re "$prompt$" { return 1} + "wrong # args" { error "$test: wrong number args"; return 0 } + timeout { error_and_restart "timeout" } + eof { api_exit; api_start; return 0 } + } +} + +proc one_line_succeed_test {command} { + global prompt + global spawn_id + global test + + send "[string trim $command]\n" + expect { + -re "OK .*$prompt$" { pass "$test"; return 1 } + -re "ERROR .*$prompt$" { + fail "$test: $expect_out(buffer)"; return 0 + } + "wrong # args" { error "$test: wrong number args"; return 0 } + timeout { fail "$test: timeout"; return 0 } + eof { fail "$test: eof"; api_exit; api_start; return 0 } + } +} + +proc one_line_fail_test {command code} { + global prompt + global spawn_id + global test + + send "[string trim $command]\n" + expect { + -re "ERROR .*$code.*$prompt$" { pass "$test"; return 1 } + -re "ERROR .*$prompt$" { fail "$test: bad failure"; return 0 } + -re "OK .*$prompt$" { fail "$test: bad success"; return 0 } + "wrong # args" { error "$test: wrong number args"; return 0 } + timeout { fail "$test: timeout"; return 0 } + eof { fail "$test: eof"; api_exit; api_start; return 0 } + } +} + +proc one_line_fail_test_nochk {command} { + global prompt + global spawn_id + global test + + send "[string trim $command]\n" + expect { + -re "ERROR .*$prompt$" { pass "$test:"; return 1 } + -re "OK .*$prompt$" { fail "$test: bad success"; return 0 } + "wrong # args" { error "$test: wrong number args"; return 0 } + timeout { fail "$test: timeout"; return 0 } + eof { fail "$test: eof"; api_exit; api_start; return 0 } + } +} + +proc resync {} { + global prompt + global spawn_id + + expect { + -re "$prompt$" {} + "wrong # args" { error "$test: wrong number args"; return 0 } + eof { api_exit; api_start } + } +} + +proc create_principal {name} { + api_exit + api_start + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_create_principal $server_handle [simple_principal \ + "%s"] {OVSEC_KADM_PRINCIPAL} "%s" + } $name $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + + return $ret +} + +proc create_policy {name} { + api_exit + api_start + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_create_policy $server_handle [simple_policy "%s"] \ + {OVSEC_KADM_POLICY} + } $name $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + + return $ret +} + +proc create_principal_pol {name policy} { + api_exit + api_start + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_create_principal $server_handle [princ_w_pol "%s" \ + "%s"] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} "%s" + } $name $policy $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + + return $ret +} + +proc delete_principal {name} { + api_exit + api_start + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_delete_principal $server_handle "%s" + } $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + + return $ret +} + +proc delete_policy {name} { + api_exit + api_start + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format {ovsec_kadm_delete_policy $server_handle "%s"} $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + + return $ret +} + +proc principal_exists {name} { + api_exit + api_start + +# puts stdout "Starting principal_exists." + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_get_principal $server_handle "%s" principal + } $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + +# puts stdout "Finishing principal_exists." + + return $ret +} + +proc policy_exists {name} { + api_exit + api_start + +# puts stdout "Starting policy_exists." + + set ret [expr { + [cmd { + ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 \ + server_handle + }] && + [cmd [format { + ovsec_kadm_get_policy $server_handle "%s" policy + } $name]] + }] + + cmd {ovsec_kadm_destroy $server_handle} + + api_exit + api_start + +# puts stdout "Finishing policy_exists." + + return $ret +} + +proc error_and_restart {error} { + api_exit + api_start + error $error +} + +proc test {name} { + global test verbose + + set test $name + if {$verbose >= 1} { + puts stdout "At $test" + } +} + +proc begin_dump {} { + global TOP + global RPC + + if { ! $RPC } { +# exec $env(SIMPLE_DUMP) > /tmp/dump.before + } +} + +proc end_dump_compare {name} { + global file + global TOP + global RPC + + if { ! $RPC } { +# set file $TOP/admin/lib/unit-test/diff-files/$name +# exec $env(SIMPLE_DUMP) > /tmp/dump.after +# exec $env(COMPARE_DUMP) /tmp/dump.before /tmp/dump.after $file + } +} + +proc kinit { princ pass {opts ""} } { + global env; + global KINIT + + eval spawn $KINIT $opts $princ + expect { + -re {Password for .*: $} + {send "$pass\n"} + timeout {puts "Timeout waiting for prompt" ; close } + } + + # this necessary so close(1) in the child will not sleep waiting for + # the parent, which is us, to read pending data. + + expect { + eof {} + } + wait +} + +proc kdestroy {} { + global KDESTROY + global errorCode errorInfo + global env + + if {[info exists errorCode]} { + set saveErrorCode $errorCode + } + if {[info exists errorInfo]} { + set saveErrorInfo $errorInfo + } + catch "system $KDESTROY 2>/dev/null" + if {[info exists saveErrorCode]} { + set errorCode $saveErrorCode + } elseif {[info exists errorCode]} { + unset errorCode + } + if {[info exists saveErrorInfo]} { + set errorInfo $saveErrorInfo + } elseif {[info exists errorInfo]} { + unset errorInfo + } +} + +proc create_principal_with_keysalts {name keysalts} { + global kadmin_local + + spawn $kadmin_local -e "$keysalts" + expect { + "kadmin.local:" {} + default { error "waiting for kadmin.local prompt"; return 1} + } + send "ank -pw \"$name\" \"$name\"\n" + expect { + -re "Principal \"$name.*\" created." {} + "kadmin.local:" { + error "expecting principal created message"; + return 1 + } + default { error "waiting for principal created message"; return 1 } + } + expect { + "kadmin.local:" {} + default { error "waiting for kadmin.local prompt"; return 1 } + } + close + wait + return 0 +} + + diff --git a/src/lib/kadm5/unit-test/lock-test.c b/src/lib/kadm5/unit-test/lock-test.c new file mode 100644 index 000000000..cff342386 --- /dev/null +++ b/src/lib/kadm5/unit-test/lock-test.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +char *whoami; + +void usage() +{ + fprintf(stderr, + "Usage: %s {shared|exclusive|permanent|release|" + "get name|wait} ...\n", whoami); + exit(1); +} + +main(int argc, char **argv) +{ + osa_adb_ret_t ret; + osa_adb_policy_t policy_db; + osa_policy_ent_t entry; + krb5_context context; + kadm5_config_params params; + + whoami = argv[0]; + + krb5_init_context(&context); + + initialize_ovk_error_table(); + initialize_adb_error_table(); + initialize_ovku_error_table(); + krb5_init_ets(context); + + params.mask = 0; + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, "while retrieving configuration parameters"); + exit(1); + } + if (! (params.mask & KADM5_CONFIG_ADBNAME)) { + com_err(whoami, KADM5_BAD_SERVER_PARAMS, + "while retrieving configuration parameters"); + exit(1); + } + + ret = osa_adb_open_policy(&policy_db, ¶ms); + if (ret != OSA_ADB_OK) { + com_err(whoami, ret, "while opening database"); + exit(1); + } + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "shared") == 0) { + ret = osa_adb_get_lock(policy_db, OSA_ADB_SHARED); + if (ret != OSA_ADB_OK) + com_err(whoami, ret, "while getting shared lock"); + else + printf("shared\n"); + } else if (strcmp(*argv, "exclusive") == 0) { + ret = osa_adb_get_lock(policy_db, OSA_ADB_EXCLUSIVE); + if (ret != OSA_ADB_OK) + com_err(whoami, ret, "while getting exclusive lock"); + else + printf("exclusive\n"); + } else if (strcmp(*argv, "permanent") == 0) { + ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT); + if (ret != OSA_ADB_OK) + com_err(whoami, ret, "while getting permanent lock"); + else + printf("permanent\n"); + } else if (strcmp(*argv, "release") == 0) { + ret = osa_adb_release_lock(policy_db); + if (ret != OSA_ADB_OK) + com_err(whoami, ret, "while releasing lock"); + else + printf("released\n"); + } else if (strcmp(*argv, "get") == 0) { + argc--; argv++; + if (!argc) usage(); + if ((ret = osa_adb_get_policy(policy_db, *argv, + &entry)) != OSA_ADB_OK) { + com_err(whoami, ret, "while getting policy"); + } else { + printf("retrieved\n"); + osa_free_policy_ent(entry); + } + } else if (strcmp(*argv, "wait") == 0) { + getchar(); + } else { + fprintf(stderr, "%s: Invalid argument \"%s\"\n", + whoami, *argv); + usage(); + } + + argc--; argv++; + } + + ret = osa_adb_close_policy(policy_db); + if (ret != OSA_ADB_OK) { + com_err(whoami, ret, "while closing database"); + exit(1); + } + + return 0; +} diff --git a/src/lib/kadm5/unit-test/randkey-test.c b/src/lib/kadm5/unit-test/randkey-test.c new file mode 100644 index 000000000..8d7e2fece --- /dev/null +++ b/src/lib/kadm5/unit-test/randkey-test.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +#define TEST_NUM 1000 + +main() +{ + ovsec_kadm_ret_t ret; + krb5_keyblock *keys[TEST_NUM]; + krb5_principal tprinc; + krb5_keyblock *newkey; + krb5_context context; + void *server_handle; + + int x, i; + + krb5_init_context(&context); + + krb5_parse_name(context, "testuser", &tprinc); + ret = ovsec_kadm_init("admin", "admin", "ovsec_adm/admin", 0, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if(ret != OVSEC_KADM_OK) { + com_err("test", ret, "init"); + exit(2); + } + for(x = 0; x < TEST_NUM; x++) { + ovsec_kadm_randkey_principal(server_handle, tprinc, &newkey); + for(i = 0; i < x; i++) { + if (!memcmp(newkey->contents, keys[i]->contents, newkey->length)) + puts("match found"); + } + krb5_copy_keyblock(context, newkey, &keys[x]); + krb5_free_keyblock(context, newkey); + } + ovsec_kadm_destroy(server_handle); + exit(0); +} + diff --git a/src/lib/kadm5/unit-test/site.exp b/src/lib/kadm5/unit-test/site.exp new file mode 100644 index 000000000..18b435dd1 --- /dev/null +++ b/src/lib/kadm5/unit-test/site.exp @@ -0,0 +1,2 @@ +set tool ovsec_kadm_srv_tcl +set prompt "% " diff --git a/src/lib/kadm5/unit-test/sizes-test.c b/src/lib/kadm5/unit-test/sizes-test.c new file mode 100644 index 000000000..5bcbd6252 --- /dev/null +++ b/src/lib/kadm5/unit-test/sizes-test.c @@ -0,0 +1,21 @@ +#include +#include +#include + +#define stringify(a) #a + +#define test_size(a,b) if (sizeof(a) != sizeof(b)) { \ + fprintf(stderr, "sizeof(%s) != sizeof(%s)\n", stringify(a), stringify(b)); \ + exit(1); \ +} + +main() +{ + test_size(unsigned long, krb5_ui_4); + test_size(long, krb5_timestamp); + test_size(long, krb5_deltat); + test_size(long, krb5_flags); + + exit(0); +} + diff --git a/src/lib/kdb/ChangeLog b/src/lib/kdb/ChangeLog index fa274c12a..4e5440cc9 100644 --- a/src/lib/kdb/ChangeLog +++ b/src/lib/kdb/ChangeLog @@ -1,3 +1,25 @@ +Fri Jul 12 15:32:26 1996 Marc Horowitz + + * kdb_cpw.c (add_key_pwd): initialize retval = 0, in case the + function is called with ks_tuple_count == 0. + +Wed Jul 10 16:22:14 1996 Marc Horowitz + + * configure.in (USE_KDB5_LIBRARY): removed. the library does not + need itself to build, and in fact fails to do so if I try. + * Makefile.in (clean-unix): remove the shared/ subdir + +Tue Jul 9 17:55:30 1996 Marc Horowitz + + * configure.in, Makefile.in: added rules and macros to do shared + library creation + +Mon Jul 8 17:06:00 1996 Barry Jaspan + + * kdb_dbm.c: Create DB_OPENCLOSE, which opens and closes the + databases for each lock. This is slower than the previous method, + but unlike the previous method it works. + Tue Jun 11 19:27:22 1996 Ezra Peisach * keytab.c (krb5_ktkdb_close): Free memory allocated by @@ -12,6 +34,24 @@ Sat May 18 15:07:09 1996 Ezra Peisach (epeisach@paris) * kdb_dbm.c: Do not provide prototypes for dbm_error or dbm_clearerr if they are really macros. +Sun May 12 01:03:07 1996 Marc Horowitz + + * kdb_xdr.c: reworked all of the krb5_dbe_* tl_data functions. + This was necessary so that the admin system could store it's own + tl_data, without needing code here. This has the side-effect of + eliminating some structures which added no value, therefore + changing about a half-dozen files elsewhere in the tree. + + * kdb_cpw.c (add_key_rnd): handle kvno incrementing in the caller, + not here. + (krb5_dbe_crk): increment the kvno here, not in add_key_rnd + (krb5_dbe_ark): increment the kvno here, not in add_key_rnd + (add_key_pwd): handle kvno incrementing in the caller, not here. + (krb5_dbe_cpw): take an arg to specify the new kvno. if it's + <= the old kvno, just increment. Otherwise, pass it to add_key_pwd. + This is why all the code in this revision was changed. + (krb5_dbe_apw): increment the kvno here, not in add_key_pwd + Tue May 7 19:48:57 1996 Ezra Peisach * t_kdb.c (do_testing): Compile if using BERK_DB and dbm is not diff --git a/src/lib/kdb/Makefile.in b/src/lib/kdb/Makefile.in index 7b1176d89..ba683222a 100644 --- a/src/lib/kdb/Makefile.in +++ b/src/lib/kdb/Makefile.in @@ -3,6 +3,10 @@ KRB5_RUN_ENV = @KRB5_RUN_ENV@ all:: $(OBJS) +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + SRCS= \ $(srcdir)/keytab.c \ $(srcdir)/encrypt_key.c \ @@ -27,6 +31,21 @@ OBJS= \ setup_mkey.o \ store_mkey.o +LIB_SUBDIRS= . +LIBDONE = DONE + +all-unix:: shared +shared:: + test -d shared || mkdir shared + +clean-unix:: + $(RM) shared/* + -rmdir shared + +DONE: $(OBJS) + $(RM) DONE + echo $(OBJS) > DONE + libkdb5.a: $(OBJS) $(RM) $@ $(ARADD) $@ $(OBJS) @@ -37,7 +56,7 @@ install:: libkdb5.a $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkdb5.a clean:: - $(RM) libkdb5.a + $(RM) libkdb5.a DONE t_kdb: t_kdb.o $(DEPLIBS) $(LD) $(LDFLAGS) $(LDARGS) -o t_kdb t_kdb.o $(LIBS) diff --git a/src/lib/kdb/configure.in b/src/lib/kdb/configure.in index ec7f93ec1..e480603e4 100644 --- a/src/lib/kdb/configure.in +++ b/src/lib/kdb/configure.in @@ -8,8 +8,9 @@ AC_HAVE_HEADERS(unistd.h) AC_CHECK_FUNCS(srand48 srand srandom umask) KRB5_RUN_FLAGS V5_USE_SHARED_LIB -USE_KDB5_LIBRARY KRB5_LIBRARIES +V5_SHARED_LIB_OBJS +V5_MAKE_SHARED_LIB(libkdb5,0.1,.., ./kdb) LinkFileDir(../libkdb5.a, libkdb5.a, ./kdb) AppendRule([all:: libkdb5.a]) AppendRule([all-unix:: ../libkdb5.a]) diff --git a/src/lib/kdb/kdb_cpw.c b/src/lib/kdb/kdb_cpw.c index 0928eba2a..54316dde0 100644 --- a/src/lib/kdb/kdb_cpw.c +++ b/src/lib/kdb/kdb_cpw.c @@ -188,7 +188,7 @@ add_key_rnd(context, master_eblock, ks_tuple, ks_tuple_count, db_entry, kvno) krb5_finish_key(context, &krbtgt_eblock); if (retval = krb5_dbekd_encrypt_key_data(context, master_eblock, - key, NULL, kvno + 1, + key, NULL, kvno, &db_entry->key_data[db_entry->n_key_data-1])) { krb5_free_keyblock(context, key); goto add_key_rnd_err; @@ -233,6 +233,9 @@ krb5_dbe_crk(context, master_eblock, ks_tuple, ks_tuple_count, db_entry) db_entry->key_data = NULL; db_entry->n_key_data = 0; + /* increment the kvno */ + kvno++; + if (retval = add_key_rnd(context, master_eblock, ks_tuple, ks_tuple_count, db_entry, kvno)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); @@ -271,15 +274,18 @@ krb5_dbe_ark(context, master_eblock, ks_tuple, ks_tuple_count, db_entry) db_entry->key_data = NULL; db_entry->n_key_data = 0; + /* increment the kvno */ + kvno++; + if (retval = add_key_rnd(context, master_eblock, ks_tuple, ks_tuple_count, db_entry, kvno)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else { - /* Copy keys with key_data_kvno = kvno */ + /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */ for (i = 0; i < key_data_count; i++) { - if (key_data[i].key_data_kvno = kvno) { + if (key_data[i].key_data_kvno == (kvno - 1)) { if (retval = krb5_dbe_create_key_data(context, db_entry)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); @@ -318,6 +324,8 @@ add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, passwd, krb5_boolean found; int i, j; + retval = 0; + for (i = 0; i < ks_tuple_count; i++) { krb5_enctype new_enctype, old_enctype; @@ -405,7 +413,7 @@ add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, passwd, if (retval = krb5_dbekd_encrypt_key_data(context, master_eblock, &key, (const krb5_keysalt *)&key_salt, - kvno + 1, &db_entry->key_data[db_entry->n_key_data-1])) { + kvno, &db_entry->key_data[db_entry->n_key_data-1])) { krb5_xfree(key.contents); return(retval); } @@ -421,28 +429,36 @@ add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, passwd, * As a side effect all old keys are nuked. */ krb5_error_code -krb5_dbe_cpw(context, master_eblock, ks_tuple, ks_tuple_count, passwd, db_entry) +krb5_dbe_cpw(context, master_eblock, ks_tuple, ks_tuple_count, passwd, + new_kvno, db_entry) krb5_context context; krb5_encrypt_block * master_eblock; krb5_key_salt_tuple * ks_tuple; int ks_tuple_count; char * passwd; + int new_kvno; krb5_db_entry * db_entry; { int key_data_count; krb5_key_data * key_data; krb5_error_code retval; - int kvno; + int old_kvno; /* First save the old keydata */ - kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); + old_kvno = get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; + /* increment the kvno. if the requested kvno is too small, + increment the old kvno */ + if (new_kvno < old_kvno+1) + new_kvno = old_kvno+1; + if (retval = add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, - passwd, db_entry, kvno)) { + passwd, db_entry, new_kvno)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; @@ -470,25 +486,29 @@ krb5_dbe_apw(context, master_eblock, ks_tuple, ks_tuple_count, passwd, db_entry) int key_data_count; krb5_key_data * key_data; krb5_error_code retval; - int kvno; + int old_kvno, new_kvno; int i; /* First save the old keydata */ - kvno = get_key_data_kvno(context, db_entry->n_key_data, db_entry->key_data); + old_kvno = get_key_data_kvno(context, db_entry->n_key_data, + db_entry->key_data); key_data_count = db_entry->n_key_data; key_data = db_entry->key_data; db_entry->key_data = NULL; db_entry->n_key_data = 0; + /* increment the kvno */ + new_kvno = old_kvno+1; + if (retval = add_key_pwd(context, master_eblock, ks_tuple, ks_tuple_count, - passwd, db_entry, kvno)) { + passwd, db_entry, new_kvno)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); db_entry->n_key_data = key_data_count; db_entry->key_data = key_data; } else { - /* Copy keys with key_data_kvno = kvno */ + /* Copy keys with key_data_kvno == old_kvno */ for (i = 0; i < key_data_count; i++) { - if (key_data[i].key_data_kvno = kvno) { + if (key_data[i].key_data_kvno == old_kvno) { if (retval = krb5_dbe_create_key_data(context, db_entry)) { cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data); diff --git a/src/lib/kdb/kdb_dbm.c b/src/lib/kdb/kdb_dbm.c index e42e31f9d..3931389f4 100644 --- a/src/lib/kdb/kdb_dbm.c +++ b/src/lib/kdb/kdb_dbm.c @@ -22,6 +22,8 @@ * */ +#define DB_OPENCLOSE + #if HAVE_UNISTD_H #include #endif @@ -341,9 +343,13 @@ krb5_dbm_db_init(context) return(retval); db_ctx = context->db_context; +#ifdef DB_OPENCLOSE + db_ctx->db_dbm_ctx = NULL; +#else if (!(db_ctx->db_dbm_ctx = (DBM *)KDBM_OPEN(db_ctx, db_ctx->db_name, O_RDWR, 0600))) return errno; +#endif if (!(filename = gen_dbsuffix (db_ctx->db_name, KDBM_LOCK_EXT(db_ctx)))) return ENOMEM; @@ -367,7 +373,9 @@ krb5_dbm_db_init(context) return 0; err_out: +#ifndef DB_OPENCLOSE KDBM_CLOSE(db_ctx, db_ctx->db_dbm_ctx); +#endif db_ctx->db_dbm_ctx = (DBM *) NULL; k5dbm_clear_context(db_ctx); return (retval); @@ -387,6 +395,7 @@ krb5_dbm_db_fini(context) db_ctx = (krb5_db_context *) context->db_context; if (k5dbm_inited(context)) { +#ifndef DB_OPENCLOSE if (db_ctx->db_dbm_ctx) { /* dbm_close returns void, but it is possible for there to be an error in close(). Possible changes to this routine: check errno @@ -395,6 +404,7 @@ krb5_dbm_db_fini(context) KDBM_CLOSE(db_ctx, db_ctx->db_dbm_ctx); db_ctx->db_dbm_ctx = NULL; } +#endif if (close(db_ctx->db_lf_file)) retval = errno; @@ -471,6 +481,8 @@ krb5_dbm_db_get_mkey(context, db_context, eblock) * * Passing a null pointer as "name" will set back to the default. * If the alternate database doesn't exist, nothing is changed. + * + * XXX rethink this */ krb5_error_code @@ -621,6 +633,17 @@ krb5_dbm_db_lock(context, mode) if ((retval = krb5_dbm_db_get_age(context, NULL, &mod_time))) goto lock_error; +#ifdef DB_OPENCLOSE + if ((db = KDBM_OPEN(db_ctx, db_ctx->db_name, + mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, + 0600))) { + db_ctx->db_lf_time = mod_time; + db_ctx->db_dbm_ctx = db; + } else { + retval = errno; + goto lock_error; + } +#else if (mod_time != db_ctx->db_lf_time) { KDBM_CLOSE(db_ctx, db_ctx->db_dbm_ctx); if ((db = KDBM_OPEN(db_ctx, db_ctx->db_name, O_RDWR, 0600))) { @@ -631,12 +654,15 @@ krb5_dbm_db_lock(context, mode) goto lock_error; } } +#endif db_ctx->db_lock_mode = mode; db_ctx->db_locks_held++; return 0; lock_error:; + db_ctx->db_lock_mode = 0; + db_ctx->db_locks_held = 0; (void) krb5_dbm_db_unlock(context); return retval; } @@ -655,6 +681,10 @@ krb5_dbm_db_unlock(context) if (!db_ctx->db_locks_held) /* lock already unlocked */ return KRB5_KDB_NOTLOCKED; +#ifdef DB_OPENCLOSE + KDBM_CLOSE(db_ctx, db_ctx->db_dbm_ctx); +#endif + if (--(db_ctx->db_locks_held) == 0) { retval = krb5_lock_file(context, db_ctx->db_lf_file, KRB5_LOCKMODE_UNLOCK); @@ -720,9 +750,8 @@ destroy_file_suffix(dbname, suffix) if (filename == 0) return ENOMEM; if ((fd = open(filename, O_RDWR, 0)) < 0) { - int retval = errno == ENOENT ? 0 : errno; free(filename); - return retval; + return errno; } /* fstat() will probably not fail unless using a remote filesystem (which is inappropriate for the kerberos database) so this check @@ -796,26 +825,40 @@ krb5_dbm_db_destroy(context, dbname) krb5_context context; char *dbname; { - krb5_error_code retval; + krb5_error_code retval1, retval2, retval3; krb5_boolean tmpcontext; tmpcontext = 0; if (!context->db_context) { tmpcontext = 1; - if ((retval = k5dbm_init_context(context))) - return(retval); + if ((retval1 = k5dbm_init_context(context))) + return(retval1); } - if (KDBM_DATA_EXT(context->db_context) && - (retval = destroy_file_suffix(dbname, - KDBM_DATA_EXT(context->db_context)))) - return(retval); - if (KDBM_INDEX_EXT(context->db_context) && - (retval = destroy_file_suffix(dbname, - KDBM_INDEX_EXT(context->db_context)))) - return(retval); - if ((retval = destroy_file_suffix(dbname, - KDBM_LOCK_EXT(context->db_context)))) - return(retval); + retval1 = retval2 = retval3 = 0; + if (KDBM_DATA_EXT(context->db_context)) + retval1 = destroy_file_suffix(dbname, + KDBM_DATA_EXT(context->db_context)); + if (KDBM_INDEX_EXT(context->db_context)) + retval2 = destroy_file_suffix(dbname, + KDBM_INDEX_EXT(context->db_context)); + retval3 = destroy_file_suffix(dbname, + KDBM_LOCK_EXT(context->db_context)); + /* + * This kludgery is needed because it is possible to link + * against BSD DB but use the ndbm interface. The result is + * that the dispatch table thinks the file extensions are + * .dir and .pag, but the database layer uses .db. + */ + if (retval1 == ENOENT && retval2 == ENOENT && + KDBM_INDEX_EXT(context->db_context) && + strcmp(KDBM_INDEX_EXT(context->db_context), ".dir") == 0 && + KDBM_DATA_EXT(context->db_context) && + strcmp(KDBM_DATA_EXT(context->db_context), ".pag") == 0) { + retval1 = retval2 = destroy_file_suffix(dbname, ".db"); + } + if (retval1 || retval2 || retval3) + return (retval1 ? retval1 : (retval2 ? retval2 : retval3)); + if (tmpcontext) { k5dbm_clear_context((krb5_db_context *) context->db_context); free(context->db_context); @@ -841,6 +884,7 @@ krb5_dbm_db_rename(context, from, to) char *from; char *to; { + DBM *db; char *fromdir = 0; char *todir = 0; char *frompag = 0; @@ -853,19 +897,28 @@ krb5_dbm_db_rename(context, from, to) s_context = context->db_context; context->db_context = (void *) NULL; if (!(retval = k5dbm_init_context(context))) { + db_ctx = (krb5_db_context *) context->db_context; + + /* + * Create the database, failing if it already exists; the + * files must exist because krb5_dbm_db_lock, called below, + * will fail otherwise. + */ + db = KDBM_OPEN(db_ctx, to, O_RDWR|O_CREAT|O_EXCL, 0600); + if (db == NULL) { + retval = errno; + goto errout; + } + else + KDBM_CLOSE(db_ctx, db); + /* * Set the database to the target, so that other processes sharing * the target will stop their activity, and notice the new database. */ - db_ctx = (krb5_db_context *) context->db_context; - retval = krb5_dbm_db_set_name(context, to); - if (retval) { - if (retval == ENOENT) - db_ctx->db_name = strdup(to); - else + if (retval) goto errout; - } db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDBM_LOCK_EXT(db_ctx)); @@ -931,8 +984,39 @@ krb5_dbm_db_rename(context, from, to) (void) unlink(fromok); retval = krb5_dbm_db_end_update(context); } else { - (void) krb5_dbm_db_end_update(context); - retval = errno; + /* + * This kludgery is needed because it is possible to link + * against BSD DB but use the ndbm interface. The result is + * that the dispatch table thinks the file extensions are + * .dir and .pag, but the database layer uses .db. + */ + if (errno == ENOENT && + KDBM_INDEX_EXT(context->db_context) && + strcmp(KDBM_INDEX_EXT(context->db_context), ".dir") == 0 && + KDBM_DATA_EXT(context->db_context) && + strcmp(KDBM_DATA_EXT(context->db_context), ".pag") == 0) { + free(fromdir); free(todir); free(frompag); free(topag); + + fromdir = todir = NULL; + frompag = gen_dbsuffix (from, ".db"); + topag = gen_dbsuffix (to, ".db"); + if (!frompag || !topag) { + retval = ENOMEM; + goto errout; + } + if (rename(frompag, topag) == 0) { + /* We only need to unlink the source lock file */ + if (fromok) + (void) unlink(fromok); + retval = krb5_dbm_db_end_update(context); + } else { + (void) krb5_dbm_db_end_update(context); + retval = errno; + } + } else { + (void) krb5_dbm_db_end_update(context); + retval = errno; + } } @@ -1082,6 +1166,7 @@ krb5_dbm_db_put_principal(context, entries, nentries) } if (KDBM_STORE(db_ctx, db_ctx->db_dbm_ctx, key, contents, DBM_REPLACE)) retval = errno; +#ifndef DB_OPENCLOSE else { DBM *db; @@ -1093,6 +1178,7 @@ krb5_dbm_db_put_principal(context, entries, nentries) else retval = errno; } +#endif krb5_free_princ_contents(context, &contents); krb5_free_princ_dbmkey(context, &key); if (retval) @@ -1166,6 +1252,7 @@ krb5_dbm_db_delete_principal(context, searchfor, nentries) else { if (KDBM_DELETE(db_ctx, db, key)) retval = errno; +#ifndef DB_OPENCLOSE else { DBM *db; @@ -1177,6 +1264,7 @@ krb5_dbm_db_delete_principal(context, searchfor, nentries) else retval = errno; } +#endif } krb5_free_princ_contents(context, &contents2); cleancontents: diff --git a/src/lib/kdb/kdb_xdr.c b/src/lib/kdb/kdb_xdr.c index 5953d6dd2..044ce4c7f 100644 --- a/src/lib/kdb/kdb_xdr.c +++ b/src/lib/kdb/kdb_xdr.c @@ -23,200 +23,223 @@ */ #include "k5-int.h" +#include #include #include +#define safe_realloc(p,n) ((p)?(realloc(p,n)):(malloc(n))) + krb5_error_code krb5_dbe_create_key_data(context, entry) krb5_context context; krb5_db_entry * entry; { - if (entry->n_key_data) { - if ((entry->key_data = (krb5_key_data *)realloc(entry->key_data, - sizeof(krb5_key_data) * (entry->n_key_data + 1)))) - memset(entry->key_data + entry->n_key_data,0,sizeof(krb5_key_data)); - else - return ENOMEM; - } else { - if ((entry->key_data = (krb5_key_data *)malloc(sizeof(krb5_key_data)))) - memset(entry->key_data, 0, sizeof(krb5_key_data)); - else - return ENOMEM; - } + if ((entry->key_data = + (krb5_key_data *) safe_realloc(entry->key_data, + (sizeof(krb5_key_data)* + (entry->n_key_data + 1)))) == NULL) + return(ENOMEM); + + + memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data)); entry->n_key_data++; + return 0; } krb5_error_code -krb5_dbe_encode_last_pwd_change(context, stamp, entry) +krb5_dbe_update_tl_data(context, entry, new_tl_data) krb5_context context; - krb5_tl_last_change * stamp; krb5_db_entry * entry; + krb5_tl_data * new_tl_data; { - krb5_tl_data ** tl_data; - krb5_octet * nextloc; - - /* Find any old versions and delete them. */ - for (tl_data = &(entry->tl_data); *tl_data; - tl_data = &((*tl_data)->tl_data_next)) { - if ((*tl_data)->tl_data_type == KRB5_TL_LAST_PWD_CHANGE) { - break; - } - } + krb5_tl_data * tl_data; + krb5_octet * tmp; + + /* copy the new data first, so we can fail cleanly if malloc() + fails */ - if ((*tl_data) || - /* Only zero data if it is freshly allocated */ - ((*tl_data) = (krb5_tl_data *)calloc(1, sizeof(krb5_tl_data)))) { - if (!(*tl_data)->tl_data_type) { - if ((nextloc = (*tl_data)->tl_data_contents = - (krb5_octet *)malloc(sizeof(krb5_timestamp))) == NULL) { - krb5_xfree(*tl_data); - (*tl_data) = NULL; - return ENOMEM; - } - (*tl_data)->tl_data_type = KRB5_TL_LAST_PWD_CHANGE; - (*tl_data)->tl_data_length = sizeof(krb5_timestamp); - entry->n_tl_data++; - } - - *nextloc++ = (krb5_octet)(stamp->last_pwd_change & 0xff); - *nextloc++ = (krb5_octet)((stamp->last_pwd_change >> 8) & 0xff); - *nextloc++ = (krb5_octet)((stamp->last_pwd_change >> 16) & 0xff); - *nextloc++ = (krb5_octet)((stamp->last_pwd_change >> 24) & 0xff); - - return 0; + if ((tmp = (krb5_octet *) malloc(new_tl_data->tl_data_length)) == NULL) + return(ENOMEM); + + /* Find an existing entry of the specified type and point at + it, or NULL if not found */ + + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) + if (tl_data->tl_data_type == new_tl_data->tl_data_type) + break; + + /* if necessary, chain a new record in the beginning and point at it */ + + if (!tl_data) { + if ((tl_data = (krb5_tl_data *) calloc(1, sizeof(krb5_tl_data))) + == NULL) { + free(tmp); + return(ENOMEM); + } + tl_data->tl_data_next = entry->tl_data; + entry->tl_data = tl_data; + entry->n_tl_data++; } - return ENOMEM; + + /* fill in the record */ + + if (tl_data->tl_data_contents) + free(tl_data->tl_data_contents); + + tl_data->tl_data_type = new_tl_data->tl_data_type; + tl_data->tl_data_length = new_tl_data->tl_data_length; + tl_data->tl_data_contents = tmp; + memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length); + + return(0); } krb5_error_code -krb5_dbe_decode_last_pwd_change(context, entry, stamp) +krb5_dbe_lookup_tl_data(context, entry, ret_tl_data) krb5_context context; krb5_db_entry * entry; - krb5_tl_last_change * stamp; + krb5_tl_data * ret_tl_data; { - krb5_tl_data * tl_data; + krb5_tl_data *tl_data; for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { - if (tl_data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE) { - krb5_octet * nextloc = tl_data->tl_data_contents; - - stamp->last_pwd_change = *nextloc++; - stamp->last_pwd_change += (*nextloc++ << 8); - stamp->last_pwd_change += (*nextloc++ << 16); - stamp->last_pwd_change += (*nextloc++ << 24); - return 0; - } + if (tl_data->tl_data_type == ret_tl_data->tl_data_type) { + *ret_tl_data = *tl_data; + return(0); + } } - stamp->last_pwd_change = 0; - return 0; + + /* if the requested record isn't found, return zero bytes. + if it ever means something to have a zero-length tl_data, + this code and its callers will have to be changed */ + + ret_tl_data->tl_data_length = 0; + ret_tl_data->tl_data_contents = NULL; + return(0); +} + +krb5_error_code +krb5_dbe_update_last_pwd_change(context, entry, stamp) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp stamp; +{ + krb5_tl_data tl_data; + krb5_octet buf[4]; /* this is the encoded size of an int32 */ + + tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + tl_data.tl_data_length = sizeof(buf); + krb5_kdb_encode_int32((krb5_int32) stamp, buf); + tl_data.tl_data_contents = buf; + + return(krb5_dbe_update_tl_data(context, entry, &tl_data)); } krb5_error_code -krb5_dbe_encode_mod_princ_data(context, mod_princ, entry) +krb5_dbe_lookup_last_pwd_change(context, entry, stamp) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp * stamp; +{ + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int32 tmp; + + tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + + if (code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)) + return(code); + + if (tl_data.tl_data_length != 4) { + *stamp = 0; + return(0); + } + + krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp); + + *stamp = (krb5_timestamp) tmp; + + return(0); +} + +/* it seems odd that there's no function to remove a tl_data, but if + I need one, I'll add one */ + +krb5_error_code +krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ) krb5_context context; - krb5_tl_mod_princ * mod_princ; krb5_db_entry * entry; + krb5_timestamp mod_date; + krb5_principal mod_princ; { + krb5_tl_data tl_data; + krb5_error_code retval = 0; - krb5_tl_data ** tl_data; krb5_octet * nextloc = 0; char * unparse_mod_princ = 0; int unparse_mod_princ_size; - /* - * Allocate *tl_data if necessary otherwise reuse it - * Need 04 bytes for date - * Need XX bytes for string - */ - if ((retval = krb5_unparse_name(context, mod_princ->mod_princ, - &unparse_mod_princ))) + if ((retval = krb5_unparse_name(context, mod_princ, + &unparse_mod_princ))) return(retval); unparse_mod_princ_size = (int) strlen(unparse_mod_princ) + 1; - if ((nextloc = malloc(unparse_mod_princ_size + 4)) == NULL) { - retval = ENOMEM; - goto cleanup; - } - - /* Find any old versions and delete them. */ - for (tl_data = &(entry->tl_data); *tl_data; - tl_data = &((*tl_data)->tl_data_next)) { - if ((*tl_data)->tl_data_type == KRB5_TL_MOD_PRINC) { - free((*tl_data)->tl_data_contents); - entry->n_tl_data--; - break; - } + if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4)) + == NULL) { + free(unparse_mod_princ); + return(ENOMEM); } - /* Allocate a new TL_MOD_PRINC structure if necessary */ - if (*tl_data == 0) { - (*tl_data) = (krb5_tl_data *)calloc(1, sizeof(krb5_tl_data)); - if (*tl_data == 0) { - retval = ENOMEM; - goto cleanup; - } - } - - entry->n_tl_data++; - (*tl_data)->tl_data_type = KRB5_TL_MOD_PRINC; - (*tl_data)->tl_data_length = unparse_mod_princ_size + 4; - (*tl_data)->tl_data_contents = nextloc; + tl_data.tl_data_type = KRB5_TL_MOD_PRINC; + tl_data.tl_data_length = unparse_mod_princ_size + 4; + tl_data.tl_data_contents = nextloc; /* Mod Date */ - krb5_kdb_encode_int32(mod_princ->mod_date, nextloc); - nextloc += 4; + krb5_kdb_encode_int32(mod_date, nextloc); /* Mod Princ */ - memcpy(nextloc, unparse_mod_princ, unparse_mod_princ_size); - nextloc = 0; + memcpy(nextloc+4, unparse_mod_princ, unparse_mod_princ_size); -cleanup: - if (nextloc) - free(nextloc); - if (unparse_mod_princ) - free(unparse_mod_princ); - return retval; + retval = krb5_dbe_update_tl_data(context, entry, &tl_data); + + free(unparse_mod_princ); + free(nextloc); + + return(retval); } krb5_error_code -krb5_dbe_decode_mod_princ_data(context, entry, mod_princ) +krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ) krb5_context context; krb5_db_entry * entry; - krb5_tl_mod_princ ** mod_princ; + krb5_timestamp * mod_time; + krb5_principal * mod_princ; { - krb5_error_code retval; - krb5_tl_data * tl_data; - krb5_octet * nextloc; + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int32 tmp; - retval = 0; - for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { - if (tl_data->tl_data_type == KRB5_TL_MOD_PRINC) { - if ((*mod_princ = malloc(sizeof(krb5_tl_mod_princ))) == NULL) - return ENOMEM; - - nextloc = tl_data->tl_data_contents; - - /* Mod Date */ - krb5_kdb_decode_int32(nextloc, (*mod_princ)->mod_date); - nextloc += 4; - - /* Mod Princ */ - if ((retval = krb5_parse_name(context, (const char *) nextloc, - &((*mod_princ)->mod_princ)))) - break; - if ((strlen((char *) nextloc) + 1 + 4) != - (size_t) tl_data->tl_data_length) { - retval = KRB5_KDB_TRUNCATED_RECORD; - break; - } - } - } + tl_data.tl_data_type = KRB5_TL_MOD_PRINC; - if (retval && (*mod_princ)) - free(*mod_princ); - return retval; + if (code = krb5_dbe_lookup_tl_data(context, entry, &tl_data)) + return(code); + + if ((tl_data.tl_data_length < 5) || + (tl_data.tl_data_contents[tl_data.tl_data_length-1] != '\0')) + return(KRB5_KDB_TRUNCATED_RECORD); + + /* Mod Date */ + krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time); + + /* Mod Princ */ + if ((code = krb5_parse_name(context, + (const char *) (tl_data.tl_data_contents+4), + mod_princ))) + return(code); + + return(0); } krb5_error_code diff --git a/src/lib/krb5/keytab/file/ChangeLog b/src/lib/krb5/keytab/file/ChangeLog index 58112dccc..39c397434 100644 --- a/src/lib/krb5/keytab/file/ChangeLog +++ b/src/lib/krb5/keytab/file/ChangeLog @@ -1,3 +1,8 @@ +Fri Jul 12 21:16:50 1996 Marc Horowitz + + * ktf_g_name.c (krb5_ktfile_get_name): include the prefix in the + returned name. + Wed Jun 12 01:09:01 1996 Theodore Ts'o * ser_ktf.c: Add #ifdef _WIN32 in places where we had #ifdef _MSDOS diff --git a/src/lib/krb5/keytab/file/ktf_g_name.c b/src/lib/krb5/keytab/file/ktf_g_name.c index a8f27c474..dd8aadc74 100644 --- a/src/lib/krb5/keytab/file/ktf_g_name.c +++ b/src/lib/krb5/keytab/file/ktf_g_name.c @@ -36,10 +36,24 @@ krb5_ktfile_get_name(context, id, name, len) /* * This routine returns the name of the name of the file associated with * this file-based keytab. name is zeroed and the filename is truncated - * to fit in name if necessary. + * to fit in name if necessary. The name is prefixed with PREFIX:, so that + * trt will happen if the name is passed back to resolve. */ { memset(name, 0, len); - strncpy(name, KTFILENAME(id), len); + + if (len < strlen(id->ops->prefix)+2) + return(ENAMETOOLONG); + strcpy(name, id->ops->prefix); + name += strlen(id->ops->prefix); + name[0] = ':'; + name++; + len -= strlen(id->ops->prefix)+1; + + if (len < strlen(KTFILENAME(id)+1)) + return(ENAMETOOLONG); + strcpy(name, KTFILENAME(id)); + /* strcpy will NUL-terminate the destination */ + return(0); } diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index 75d2f0d3e..721ce1e5e 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,8 @@ +Fri Jul 12 21:38:15 1996 Marc Horowitz + + * ktdefname.c (krb5_kt_default_name): allow the default keytab name + to be specified in the config file. + Wed Jun 12 01:12:32 1996 Theodore Ts'o * net_read.c (krb5_net_read): diff --git a/src/lib/krb5/os/ktdefname.c b/src/lib/krb5/os/ktdefname.c index 433c1c9da..29eb54fe2 100644 --- a/src/lib/krb5/os/ktdefname.c +++ b/src/lib/krb5/os/ktdefname.c @@ -37,12 +37,22 @@ krb5_kt_default_name(context, name, namesize) int namesize; { char *cp = 0; + krb5_error_code code; + char *retval; - if (context->profile_secure == FALSE) cp = getenv("KRB5_KTNAME"); - if (cp) { + if ((context->profile_secure == FALSE) && + (cp = getenv("KRB5_KTNAME"))) { strncpy(name, cp, namesize); if (strlen(cp) >= (size_t) namesize) return KRB5_CONFIG_NOTENUFSPACE; + } else if (((code = profile_get_string(context->profile, + "libdefaults", + "default_keytab_name", NULL, + NULL, &retval)) == 0) && + retval) { + strncpy(name, retval, namesize); + if ((size_t) namesize < strlen(retval)) + return KRB5_CONFIG_NOTENUFSPACE; } else { #if defined (_MSDOS) || defined(_WIN32) { diff --git a/src/lib/rpc/ChangeLog b/src/lib/rpc/ChangeLog new file mode 100644 index 000000000..a78d05c37 --- /dev/null +++ b/src/lib/rpc/ChangeLog @@ -0,0 +1,20 @@ +Fri Jul 12 15:33:50 1996 Marc Horowitz + + * rpc_dtablesize.c (_rpc_dtablesize): put in a few checks to make + sure that the return value is never larger than FD_SETSIZE, since + this function's purpose is to be used as the first arg to + select(). + +Tue Jul 9 17:56:54 1996 Marc Horowitz + + * rpc.h, netdb.h, getrpcent.c: Our build can (and will) require + that the rpc header files shipped with kerberos be used if the + library shipped with kerberos is used. Thus, some simplifying + assumptions can be made, mostly having to do with the declaration + of struct rpcent and the related functions. + * clnt_perror.c: made usage of sys_errlist conditional on + NEED_SYS_ERRLIST + * configure.in (DECLARE_SYS_ERRLIST): added + * Makefile.in (DONE): added a few rules and variable so shared + library creation would work + diff --git a/src/lib/rpc/Makefile.in b/src/lib/rpc/Makefile.in new file mode 100644 index 000000000..9426bb94c --- /dev/null +++ b/src/lib/rpc/Makefile.in @@ -0,0 +1,148 @@ +CFLAGS = $(CCOPTS) $(DEFS) -DDEBUG_GSSAPI -I$(srcdir)/.. + +##DOSBUILDTOP = ..\.. +##DOSLIBNAME=libgssrpc.lib + +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + +SRCS = $(srcdir)/auth_none.c \ + $(srcdir)/auth_unix.c \ + $(srcdir)/auth_any.c \ + $(srcdir)/authunix_prot.c \ + $(srcdir)/auth_gssapi.c \ + $(srcdir)/auth_gssapi_misc.c \ + $(srcdir)/bindresvport.c \ + $(srcdir)/clnt_generic.c \ + $(srcdir)/clnt_perror.c \ + $(srcdir)/clnt_raw.c \ + $(srcdir)/clnt_simple.c \ + $(srcdir)/clnt_tcp.c \ + $(srcdir)/clnt_udp.c \ + $(srcdir)/rpc_dtablesize.c \ + $(srcdir)/get_myaddress.c \ + $(srcdir)/getrpcent.c \ + $(srcdir)/getrpcport.c \ + $(srcdir)/pmap_clnt.c \ + $(srcdir)/pmap_getmaps.c \ + $(srcdir)/pmap_getport.c \ + $(srcdir)/pmap_prot.c \ + $(srcdir)/pmap_prot2.c \ + $(srcdir)/pmap_rmt.c \ + $(srcdir)/rpc_prot.c \ + $(srcdir)/rpc_commondata.c \ + $(srcdir)/rpc_callmsg.c \ + $(srcdir)/svc.c \ + $(srcdir)/svc_auth.c \ + $(srcdir)/svc_auth_unix.c \ + $(srcdir)/svc_auth_any.c \ + $(srcdir)/svc_auth_gssapi.c \ + $(srcdir)/svc_raw.c \ + $(srcdir)/svc_run.c \ + $(srcdir)/svc_simple.c \ + $(srcdir)/svc_tcp.c \ + $(srcdir)/svc_udp.c \ + $(srcdir)/xdr.c \ + $(srcdir)/xdr_array.c \ + $(srcdir)/xdr_float.c \ + $(srcdir)/xdr_mem.c \ + $(srcdir)/xdr_rec.c \ + $(srcdir)/xdr_reference.c \ + $(srcdir)/xdr_stdio.c \ + $(srcdir)/xdr_alloc.c + +OBJS = auth_none.$(OBJEXT) \ + auth_unix.$(OBJEXT) \ + auth_any.$(OBJEXT) \ + authunix_prot.$(OBJEXT) \ + auth_gssapi.$(OBJEXT) \ + auth_gssapi_misc.$(OBJEXT) \ + bindresvport.$(OBJEXT) \ + clnt_generic.$(OBJEXT) \ + clnt_perror.$(OBJEXT) \ + clnt_raw.$(OBJEXT) \ + clnt_simple.$(OBJEXT) \ + clnt_tcp.$(OBJEXT) \ + clnt_udp.$(OBJEXT) \ + rpc_dtablesize.$(OBJEXT) \ + get_myaddress.$(OBJEXT) \ + getrpcent.$(OBJEXT) \ + getrpcport.$(OBJEXT) \ + pmap_clnt.$(OBJEXT) \ + pmap_getmaps.$(OBJEXT) \ + pmap_getport.$(OBJEXT) \ + pmap_prot.$(OBJEXT) \ + pmap_prot2.$(OBJEXT) \ + pmap_rmt.$(OBJEXT) \ + rpc_prot.$(OBJEXT) \ + rpc_commondata.$(OBJEXT) \ + rpc_callmsg.$(OBJEXT) \ + svc.$(OBJEXT) \ + svc_auth.$(OBJEXT) \ + svc_auth_unix.$(OBJEXT) \ + svc_auth_any.$(OBJEXT) \ + svc_auth_gssapi.$(OBJEXT) \ + svc_raw.$(OBJEXT) \ + svc_run.$(OBJEXT) \ + svc_simple.$(OBJEXT) \ + svc_tcp.$(OBJEXT) \ + svc_udp.$(OBJEXT) \ + xdr.$(OBJEXT) \ + xdr_array.$(OBJEXT) \ + xdr_float.$(OBJEXT) \ + xdr_mem.$(OBJEXT) \ + xdr_rec.$(OBJEXT) \ + xdr_reference.$(OBJEXT) \ + xdr_stdio.$(OBJEXT) \ + xdr_alloc.$(OBJEXT) + +LIB_SUBDIRS= . +LIBDONE= DONE +# +# Depends on libgssapi_krb5, libkrb5, libcrypto, libcom_err +# +GSSAPI_KRB5_VER=@GSSAPI_KRB5_SH_VERS@ +KRB5_VER=@KRB5_SH_VERS@ +CRYPTO_VER=@CRYPTO_SH_VERS@ +COMERR_VER=@COMERR_SH_VERS@ +DEPLIBS=$(TOPLIBD)/libgssapi_krb5.$(SHEXT).$(COMERR_VER) \ + $(TOPLIBD)/libkrb5.$(SHEXT).$(KRB5_VER) \ + $(TOPLIBD)/libcrypto.$(SHEXT).$(CRYPTO_VER) \ + $(TOPLIBD)/libcom_err.$(SHEXT).$(COMERR_VER) + +SHLIB_LIBS=-lkrb5 -lcrypto -lcom_err -ldyn +SHLIB_LDFLAGS= $(LDFLAGS) @SHLIB_RPATH_DIRS@ +SHLIB_LIBDIRS= @SHLIB_LIBDIRS@ + +all-unix:: shared includes $(OBJS) +all-mac:: $(OBJS) +all-windows:: $(OBJS) + +shared: + mkdir shared + +check-windows:: + +clean-unix:: + $(RM) shared/* + +clean-mac:: +clean-windows:: + +DONE: $(OBJS) + $(RM) DONE + echo $(OBJS) > DONE + +libgssrpc.$(STEXT): $(OBJS) + $(RM) $@ + $(ARADD) $@ $(OBJS) + $(RANLIB) $@ + +install:: libgssrpc.a + $(INSTALL_DATA) libgssrpc.a $(DESTDIR)$(KRB5_LIBDIR)/libgssrpc.a + $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libgssrpc.a + +clean:: + $(RM) libgssrpc.$(LIBEXT) libgssrpc.bak DONE + diff --git a/src/lib/rpc/Makefile.ov b/src/lib/rpc/Makefile.ov new file mode 100644 index 000000000..d64c77e22 --- /dev/null +++ b/src/lib/rpc/Makefile.ov @@ -0,0 +1,52 @@ +# +# Makefile for librpclib.a. +# +# $Id$ +# $Source$ +# +TOP = .. +include $(TOP)/config.mk/template + +SUBDIRS = unit-test + +SRCS = auth_none.c auth_unix.c auth_any.c authunix_prot.c \ + auth_gssapi.c auth_gssapi_misc.c bindresvport.c \ + clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c clnt_tcp.c \ + clnt_udp.c rpc_dtablesize.c get_myaddress.c getrpcent.c getrpcport.c \ + pmap_clnt.c pmap_getmaps.c pmap_getport.c pmap_prot.c \ + pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c rpc_callmsg.c \ + svc.c svc_auth.c svc_auth_unix.c svc_auth_any.c svc_auth_gssapi.c \ + svc_raw.c svc_run.c svc_simple.c \ + svc_tcp.c svc_udp.c xdr.c xdr_array.c xdr_float.c xdr_mem.c \ + xdr_rec.c xdr_reference.c xdr_stdio.c xdr_alloc.c + +OBJS = auth_none.o auth_unix.o auth_any.o authunix_prot.o \ + auth_gssapi.o auth_gssapi_misc.o bindresvport.o \ + clnt_generic.o clnt_perror.o clnt_raw.o clnt_simple.o clnt_tcp.o \ + clnt_udp.o rpc_dtablesize.o get_myaddress.o getrpcent.o getrpcport.o \ + pmap_clnt.o pmap_getmaps.o pmap_getport.o pmap_prot.o \ + pmap_prot2.o pmap_rmt.o rpc_prot.o rpc_commondata.o rpc_callmsg.o \ + svc.o svc_auth.o svc_auth_unix.o svc_auth_any.o svc_auth_gssapi.o \ + svc_raw.o svc_run.o svc_simple.o \ + svc_tcp.o svc_udp.o xdr.o xdr_array.o xdr_float.o xdr_mem.o \ + xdr_rec.o xdr_reference.o xdr_stdio.o xdr_alloc.o + +HDRS = auth.h auth_unix.h auth_gssapi.h clnt.h pmap_clnt.h \ + pmap_prot.h pmap_rmt.h rpc.h rpc_msg.h svc.h svc_auth.h types.h xdr.h + +HDRS_DIR = rpc + +LIB = librpclib.a + +CFLAGS := -I.. $(CFLAGS) -DDEBUG_GSSAPI=0 $(D_NEEDS_RPCENT) + +expand StageLibrary + +expand StageIncludes + +expand Depend + +expand SubdirTarget + +expand Saber + diff --git a/src/lib/rpc/auth.h b/src/lib/rpc/auth.h new file mode 100644 index 000000000..4b0a40ccc --- /dev/null +++ b/src/lib/rpc/auth.h @@ -0,0 +1,197 @@ +/* @(#)auth.h 2.3 88/08/07 4.0 RPCSRC; from 1.17 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * auth.h, Authentication interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The data structures are completely opaque to the client. The client + * is required to pass a AUTH * to routines that create rpc + * "sessions". + */ + + +#define MAX_AUTH_BYTES 400 +#define MAXNETNAMELEN 255 /* maximum length of network user's name */ + +/* + * Status returned from authentication check + */ +enum auth_stat { + AUTH_OK=0, + /* + * failed at remote end + */ + AUTH_BADCRED=1, /* bogus credentials (seal broken) */ + AUTH_REJECTEDCRED=2, /* client should begin new session */ + AUTH_BADVERF=3, /* bogus verifier (seal broken) */ + AUTH_REJECTEDVERF=4, /* verifier expired or was replayed */ + AUTH_TOOWEAK=5, /* rejected due to security reasons */ + /* + * failed locally + */ + AUTH_INVALIDRESP=6, /* bogus response verifier */ + AUTH_FAILED=7 /* some unknown reason */ +}; + +union des_block { +#if 0 /* XXX nothing uses this, anyway */ + struct { + rpc_u_int32 high; + rpc_u_int32 low; + } key; +#endif + char c[8]; +}; +typedef union des_block des_block; +extern bool_t xdr_des_block(); + +/* + * Authentication info. Opaque to client. + */ +struct opaque_auth { + enum_t oa_flavor; /* flavor of auth */ + caddr_t oa_base; /* address of more auth stuff */ + unsigned int oa_length; /* not to exceed MAX_AUTH_BYTES */ +}; + + +/* + * Auth handle, interface to client side authenticators. + */ +typedef struct { + struct opaque_auth ah_cred; + struct opaque_auth ah_verf; + union des_block ah_key; + struct auth_ops { + void (*ah_nextverf)(); + int (*ah_marshal)(); /* nextverf & serialize */ + int (*ah_validate)(); /* validate varifier */ + int (*ah_refresh)(); /* refresh credentials */ + void (*ah_destroy)(); /* destroy this structure */ + int (*ah_wrap)(); /* encode data for wire */ + int (*ah_unwrap)(); /* decode data from wire */ + } *ah_ops; + caddr_t ah_private; +} AUTH; + + +/* + * Authentication ops. + * The ops and the auth handle provide the interface to the authenticators. + * + * AUTH *auth; + * XDR *xdrs; + * struct opaque_auth verf; + */ +#define AUTH_NEXTVERF(auth) \ + ((*((auth)->ah_ops->ah_nextverf))(auth)) +#define auth_nextverf(auth) \ + ((*((auth)->ah_ops->ah_nextverf))(auth)) + +#define AUTH_MARSHALL(auth, xdrs) \ + ((*((auth)->ah_ops->ah_marshal))(auth, xdrs)) +#define auth_marshall(auth, xdrs) \ + ((*((auth)->ah_ops->ah_marshal))(auth, xdrs)) + +#define AUTH_VALIDATE(auth, verfp) \ + ((*((auth)->ah_ops->ah_validate))((auth), verfp)) +#define auth_validate(auth, verfp) \ + ((*((auth)->ah_ops->ah_validate))((auth), verfp)) + +#define AUTH_REFRESH(auth, msg) \ + ((*((auth)->ah_ops->ah_refresh))(auth, msg)) +#define auth_refresh(auth, msg) \ + ((*((auth)->ah_ops->ah_refresh))(auth, msg)) + +#define AUTH_WRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->ah_ops->ah_wrap))(auth, xdrs, \ + xfunc, xwhere)) +#define AUTH_WRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->ah_ops->ah_wrap))(auth, xdrs, \ + xfunc, xwhere)) +#define AUTH_UNWRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->ah_ops->ah_unwrap))(auth, xdrs, \ + xfunc, xwhere)) +#define AUTH_UNWRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->ah_ops->ah_unwrap))(auth, xdrs, \ + xfunc, xwhere)) + +#define AUTH_DESTROY(auth) \ + ((*((auth)->ah_ops->ah_destroy))(auth)) +#define auth_destroy(auth) \ + ((*((auth)->ah_ops->ah_destroy))(auth)) + + +extern struct opaque_auth _null_auth; + + +/* + * These are the various implementations of client side authenticators. + */ + +/* + * Any style authentication. These routines can be used by any + * authentication style that does not use the wrap/unwrap functions. + */ +int authany_wrap(), authany_unwrap(); + +/* + * Unix style authentication + * AUTH *authunix_create(machname, uid, gid, len, aup_gids) + * char *machname; + * int uid; + * int gid; + * int len; + * int *aup_gids; + */ +extern AUTH *authunix_create(); +extern AUTH *authunix_create_default(); /* takes no parameters */ +extern AUTH *authnone_create(); /* takes no parameters */ +extern AUTH *authdes_create(); + +/* + * GSS-API style authentication: + * see + */ + +#define AUTH_NONE 0 /* no authentication */ +#define AUTH_NULL 0 /* backward compatibility */ +#define AUTH_UNIX 1 /* unix style (uid, gids) */ +#define AUTH_SHORT 2 /* short hand unix style */ +#define AUTH_DES 3 /* des style (encrypted timestamps) */ +#define AUTH_GSSAPI 300001 /* GSS-API style */ + +/* + * BACKWARDS COMPATIBILIY! OpenV*Secure 1.0 had AUTH_GSSAPI == 4. We + * need to accept this value until 1.0 is dead. + */ +#define AUTH_GSSAPI_COMPAT 4 diff --git a/src/lib/rpc/auth_any.c b/src/lib/rpc/auth_any.c new file mode 100644 index 000000000..5e2a8633a --- /dev/null +++ b/src/lib/rpc/auth_any.c @@ -0,0 +1,19 @@ +/* + * auth_any.c + * Provides default functions for authentication flavors that do not + * use all the fields in structauth_ops. + */ + +#include +#include +#include +#include + +int authany_wrap(auth, xdrs, xfunc, xwhere) + AUTH *auth; + XDR *xdrs; + xdrproc_t xfunc; + caddr_t xwhere; +{ + return (*xfunc)(xdrs, xwhere); +} diff --git a/src/lib/rpc/auth_gssapi.c b/src/lib/rpc/auth_gssapi.c new file mode 100644 index 000000000..0ffe96d18 --- /dev/null +++ b/src/lib/rpc/auth_gssapi.c @@ -0,0 +1,901 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * $Log$ + * Revision 1.23 1996/07/22 20:39:39 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.22.4.1 1996/07/18 04:18:29 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.22.2.1 1996/06/20 23:35:31 marc + * File added to the repository on a branch + * + * Revision 1.22 1996/06/05 20:56:16 bjaspan + * memset bindings to zero before use + * + * Revision 1.21 1996/05/12 06:11:38 marc + * renamed lots of types: u_foo to unsigned foo, and foo32 to rpc_foo32. This is to make autoconfiscation less painful. + * + * Revision 1.20 1995/12/13 14:02:45 grier + * Longs to ints for Alpha + * + * Revision 1.19 1995/10/31 16:36:00 bjaspan + * GS reported this bug. When talking to a 1.1 server, the server will + * not fail indicating the version is wrong because it never checks the + * version; it just responds with call_res.version 1. So we have to + * deal. Oops. + * + * It's amazing what can fall through the cracks when most of the + * development team is fired.. + * + * Revision 1.18 1995/10/31 16:07:06 bjaspan + * fix from grier + * + * Revision 1.17 1995/08/24 21:06:26 bjaspan + * set acceptor channel bindings + * + * Revision 1.16 1995/08/23 20:27:37 bjaspan + * [secure-rpc/3392] add channel bindinds to the rpc + * + * Revision 1.15 1995/05/08 22:32:01 marc + * change call_arg.version from 1 to 2 to indicate the new client is in use. + * + * Revision 1.14 1995/03/22 22:05:20 jik + * Reorder the auth_ops structure, to agree with the order of the version of + * this library in the GK source tree. I chose to reorder this one rather + * than reorder the GK tree because Barry says that the order in the GK tree + * makes more sense. + * + * Revision 1.13 1994/10/27 12:39:02 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] add version field to client creds + * + * change to use GSS_ERROR &c macros; I don't think this is correct + * ============================================================================= + * + * Revision 1.14 1994/10/26 20:03:46 bjaspan + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.13 1994/04/07 16:12:06 jik + * The second argument to xdr_opaque_auth is struct auth *, not struct + * auth. + * + * Revision 1.12 1993/12/08 21:42:43 bjaspan + * use AUTH_GSSAPI_DISPLAY_STATUS macro, reindent + * + * Revision 1.11 1993/12/06 21:21:03 bjaspan + * debugging levels + * + * Revision 1.10 1993/11/18 23:13:07 bjaspan + * add some function comments + * + * Revision 1.9 1993/11/15 19:50:03 bjaspan + * redefine AUTH_REFRESH to take the error message as an argument, and + * change auth_gssapi_refresh to increment the seq_num on REJECTEDVERF + * + * Revision 1.8 1993/11/12 02:31:59 bjaspan + * set rpc_createerr as appropriate + * + * Revision 1.7 1993/11/03 21:21:02 bjaspan + * fix seq_num handling in cases of errors and retransmission + * + * Revision 1.6 1993/11/01 19:55:20 bjaspan + * display gss_major and gss_minor, and unstatic auth_gssapi_debug + * + * Revision 1.5 1993/10/28 22:07:33 bjaspan + * create_default takes char *, use seq_num in args/results + * + * Revision 1.4 1993/10/26 21:11:53 bjaspan + * working + * + * Revision 1.3 1993/10/21 19:01:09 bjaspan + * added auth_gssapi_destroy, cleaned up a few things + * + * Revision 1.2 1993/10/19 03:10:58 bjaspan + * snapshot: GSS-API working in hacked up state, not seal/unseal + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef __CODECENTER__ +#define DEBUG_GSSAPI 1 +#endif + +#ifdef DEBUG_GSSAPI +int auth_debug_gssapi = DEBUG_GSSAPI; +#define L_PRINTF(l,args) if (auth_debug_gssapi >= l) printf args +#define PRINTF(args) L_PRINTF(99, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) \ + if (auth_debug_gssapi) auth_gssapi_display_status args +#else +#define PRINTF(args) +#define L_PRINTF(l, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) +#endif + +static void auth_gssapi_nextverf(); +static bool_t auth_gssapi_marshall(); +static bool_t auth_gssapi_validate(); +static bool_t auth_gssapi_refresh(); +static bool_t auth_gssapi_wrap(); +static bool_t auth_gssapi_unwrap(); +static void auth_gssapi_destroy(); + +static bool_t marshall_new_creds(); + +static struct auth_ops auth_gssapi_ops = { + auth_gssapi_nextverf, + auth_gssapi_marshall, + auth_gssapi_validate, + auth_gssapi_refresh, + auth_gssapi_destroy, + auth_gssapi_wrap, + auth_gssapi_unwrap, +}; + +/* + * the ah_private data structure for an auth_handle + */ +struct auth_gssapi_data { + bool_t established; + CLIENT *clnt; + gss_ctx_id_t context; + gss_buffer_desc client_handle; + rpc_u_int32 seq_num; + int def_cred; + + /* pre-serialized ah_cred */ + unsigned char cred_buf[MAX_AUTH_BYTES]; + rpc_u_int32 cred_len; +}; +#define AUTH_PRIVATE(auth) ((struct auth_gssapi_data *)auth->ah_private) + +extern struct rpc_createerr rpc_createerr; + +/* + * Function: auth_gssapi_create_default + * + * Purpose: Create a GSS-API style authenticator, with default + * options, and return the handle. + * + * Effects: See design document, section XXX. + */ +AUTH *auth_gssapi_create_default(CLIENT *clnt, char *service_name) +{ + AUTH *auth; + OM_uint32 gssstat, minor_stat; + gss_buffer_desc input_name; + gss_name_t target_name; + + input_name.value = service_name; + input_name.length = strlen(service_name) + 1; + + gssstat = gss_import_name(&minor_stat, &input_name, + gss_nt_service_name, &target_name); + if (gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("parsing name", gssstat, + minor_stat)); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = ENOMEM; + return NULL; + } + + auth = auth_gssapi_create(clnt, + &gssstat, + &minor_stat, + GSS_C_NO_CREDENTIAL, + target_name, + GSS_C_NULL_OID, + GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, + 0, + NULL, + NULL, + NULL); + + gss_release_name(&minor_stat, &target_name); + return auth; +} + +/* + * Function: auth_gssapi_create + * + * Purpose: Create a GSS-API style authenticator, with all the + * options, and return the handle. + * + * Effects: See design document, section XXX. + */ +AUTH *auth_gssapi_create(CLIENT *clnt, + OM_uint32 *gssstat, + OM_uint32 *minor_stat, + gss_cred_id_t claimant_cred_handle, + gss_name_t target_name, + gss_OID mech_type, + int req_flags, + OM_uint32 time_req, + gss_OID *actual_mech_type, + int *ret_flags, + OM_uint32 *time_rec) +{ + AUTH *auth, *save_auth; + struct auth_gssapi_data *pdata; + struct gss_channel_bindings_struct bindings, *bindp; + struct sockaddr_in laddr, raddr; + enum clnt_stat callstat; + struct timeval timeout; + int init_func; + + auth_gssapi_init_arg call_arg; + auth_gssapi_init_res call_res; + gss_buffer_desc *input_token, isn_buf; + + memset(&rpc_createerr, 0, sizeof(rpc_createerr)); + + /* this timeout is only used if clnt_control(clnt, CLSET_TIMEOUT) */ + /* has not already been called.. therefore, we can just pick */ + /* something reasonable-sounding.. */ + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + auth = NULL; + pdata = NULL; + + auth = (AUTH *) malloc(sizeof(*auth)); + pdata = (struct auth_gssapi_data *) malloc(sizeof(*pdata)); + if (auth == NULL || pdata == NULL) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = ENOMEM; + goto cleanup; + } + memset((char *) auth, 0, sizeof(*auth)); + memset((char *) pdata, 0, sizeof(*pdata)); + + auth->ah_ops = &auth_gssapi_ops; + auth->ah_private = (caddr_t) pdata; + + /* initial creds are auth_msg TRUE and no handle */ + marshall_new_creds(auth, TRUE, NULL); + + /* initial verifier is empty */ + auth->ah_verf.oa_flavor = AUTH_GSSAPI; + auth->ah_verf.oa_base = NULL; + auth->ah_verf.oa_length = 0; + + AUTH_PRIVATE(auth)->established = FALSE; + AUTH_PRIVATE(auth)->clnt = clnt; + AUTH_PRIVATE(auth)->def_cred = (claimant_cred_handle == + GSS_C_NO_CREDENTIAL); + + /* don't assume the caller will want to change clnt->cl_auth */ + save_auth = clnt->cl_auth; + clnt->cl_auth = auth; + + /* start by trying latest version */ + call_arg.version = 3; + +try_new_version: + /* set state for initial call to init_sec_context */ + input_token = GSS_C_NO_BUFFER; + AUTH_PRIVATE(auth)->context = GSS_C_NO_CONTEXT; + init_func = AUTH_GSSAPI_INIT; + + if (call_arg.version == 3) { + if (clnt_control(clnt, CLGET_LOCAL_ADDR, &laddr) == FALSE) { + PRINTF(("gssapi_create: CLGET_LOCAL_ADDR failed")); + goto cleanup; + } + if (clnt_control(clnt, CLGET_SERVER_ADDR, &raddr) == FALSE) { + PRINTF(("gssapi_create: CLGET_SERVER_ADDR failed")); + goto cleanup; + } + + memset(&bindings, 0, sizeof(bindings)); + bindings.application_data.length = 0; + bindings.initiator_addrtype = GSS_C_AF_INET; + bindings.initiator_address.length = 4; + bindings.initiator_address.value = &laddr.sin_addr.s_addr; + + bindings.acceptor_addrtype = GSS_C_AF_INET; + bindings.acceptor_address.length = 4; + bindings.acceptor_address.value = &raddr.sin_addr.s_addr; + bindp = &bindings; + } else { + bindp = NULL; + } + + memset((char *) &call_res, 0, sizeof(call_res)); + +next_token: + *gssstat = gss_init_sec_context(minor_stat, + claimant_cred_handle, + &AUTH_PRIVATE(auth)->context, + target_name, + mech_type, + req_flags, + time_req, + bindp, + input_token, + actual_mech_type, + &call_arg.token, + ret_flags, + time_rec); + + if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) { + AUTH_GSSAPI_DISPLAY_STATUS(("initializing context", *gssstat, + *minor_stat)); + goto cleanup; + } + + /* if we got a token, pass it on */ + if (call_arg.token.length != 0) { + + /* + * sanity check: if we received a signed isn in the last + * response then there *cannot* be another token to send + */ + if (call_res.signed_isn.length != 0) { + PRINTF(("gssapi_create: unexpected token from init_sec\n")); + goto cleanup; + } + + PRINTF(("gssapi_create: calling GSSAPI_INIT (%d)\n", init_func)); + + memset((char *) &call_res, 0, sizeof(call_res)); + callstat = clnt_call(clnt, init_func, + xdr_authgssapi_init_arg, &call_arg, + xdr_authgssapi_init_res, &call_res, + timeout); + gss_release_buffer(minor_stat, &call_arg.token); + + if (callstat != RPC_SUCCESS) { + struct rpc_err err; + + clnt_geterr(clnt, &err); + if (callstat == RPC_AUTHERROR && + (err.re_why == AUTH_BADCRED || err.re_why == AUTH_FAILED) + && call_arg.version >= 1) { + L_PRINTF(1, ("call_arg protocol " + "version %d rejected, trying %d.\n", + call_arg.version, call_arg.version-1)); + call_arg.version--; + goto try_new_version; + } else { + PRINTF(("gssapi_create: GSSAPI_INIT (%d) failed, stat %d\n", + init_func, callstat)); + } + + goto cleanup; + } else if (call_res.version != call_arg.version && + !(call_arg.version == 2 && call_res.version == 1)) { + /* + * The Secure 1.1 servers always respond with version + * 1. Thus, if we just tried a version >=3, fall all + * the way back to version 1 since that is all they + * understand + */ + if (call_arg.version > 2 && call_res.version == 1) { + L_PRINTF(1, ("Talking to Secure 1.1 server, " + "using version 1.\n")); + call_arg.version = 1; + goto try_new_version; + } + + PRINTF(("gssapi_create: invalid call_res vers %d\n", + call_res.version)); + goto cleanup; + } else if (call_res.gss_major != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("in response from server", + call_res.gss_major, + call_res.gss_minor)); + goto cleanup; + } + + PRINTF(("gssapi_create: GSSAPI_INIT (%d) succeeded\n", init_func)); + init_func = AUTH_GSSAPI_CONTINUE_INIT; + + /* check for client_handle */ + if (AUTH_PRIVATE(auth)->client_handle.length == 0) { + if (call_res.client_handle.length == 0) { + PRINTF(("gssapi_create: expected client_handle\n")); + goto cleanup; + } else { + PRINTF(("gssapi_create: got client_handle %d\n", + *((rpc_u_int32 *)call_res.client_handle.value))); + + GSS_DUP_BUFFER(AUTH_PRIVATE(auth)->client_handle, + call_res.client_handle); + + /* auth_msg is TRUE; there may be more tokens */ + marshall_new_creds(auth, TRUE, + &AUTH_PRIVATE(auth)->client_handle); + } + } else if (!GSS_BUFFERS_EQUAL(AUTH_PRIVATE(auth)->client_handle, + call_res.client_handle)) { + PRINTF(("gssapi_create: got different client_handle\n")); + goto cleanup; + } + + /* check for token */ + if (call_res.token.length==0 && *gssstat==GSS_S_CONTINUE_NEEDED) { + PRINTF(("gssapi_create: expected token\n")); + goto cleanup; + } else if (call_res.token.length != 0) { + if (*gssstat == GSS_S_COMPLETE) { + PRINTF(("gssapi_create: got unexpected token\n")); + goto cleanup; + } else { + /* assumes call_res is safe until init_sec_context */ + input_token = &call_res.token; + PRINTF(("gssapi_create: got new token\n")); + } + } + } + + /* check for isn */ + if (*gssstat == GSS_S_COMPLETE) { + if (call_res.signed_isn.length == 0) { + PRINTF(("gssapi_created: expected signed isn\n")); + goto cleanup; + } else { + PRINTF(("gssapi_create: processing signed isn\n")); + + /* don't check conf (integ only) or qop (accpet default) */ + *gssstat = gss_unseal(minor_stat, + AUTH_PRIVATE(auth)->context, + &call_res.signed_isn, + &isn_buf, NULL, NULL); + + if (*gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("unsealing isn", + *gssstat, *minor_stat)); + goto cleanup; + } else if (isn_buf.length != sizeof(rpc_u_int32)) { + PRINTF(("gssapi_create: gss_unseal gave %d bytes\n", + isn_buf.length)); + goto cleanup; + } + + AUTH_PRIVATE(auth)->seq_num = (rpc_u_int32) + ntohl(*((rpc_u_int32*)isn_buf.value)); + *gssstat = gss_release_buffer(minor_stat, &isn_buf); + if (*gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("releasing unsealed isn", + *gssstat, *minor_stat)); + goto cleanup; + } + + PRINTF(("gssapi_create: isn is %d\n", + AUTH_PRIVATE(auth)->seq_num)); + + /* we no longer need these results.. */ + xdr_free(xdr_authgssapi_init_res, &call_res); + } + } else if (call_res.signed_isn.length != 0) { + PRINTF(("gssapi_create: got signed isn, can't check yet\n")); + } + + /* results were okay.. continue if necessary */ + if (*gssstat == GSS_S_CONTINUE_NEEDED) { + PRINTF(("gssapi_create: not done, continuing\n")); + goto next_token; + } + + /* + * Done! Context is established, we have client_handle and isn. + */ + AUTH_PRIVATE(auth)->established = TRUE; + + marshall_new_creds(auth, FALSE, + &AUTH_PRIVATE(auth)->client_handle); + + PRINTF(("gssapi_create: done. client_handle %#x, isn %d\n\n", + *((rpc_u_int32 *)AUTH_PRIVATE(auth)->client_handle.value), + AUTH_PRIVATE(auth)->seq_num)); + + /* don't assume the caller will want to change clnt->cl_auth */ + clnt->cl_auth = save_auth; + + return auth; + + /******************************************************************/ + +cleanup: + PRINTF(("gssapi_create: bailing\n\n")); + + if (AUTH_PRIVATE(auth)) + auth_gssapi_destroy(auth); + else if (auth) + free(auth); + auth = NULL; + + /* don't assume the caller will want to change clnt->cl_auth */ + clnt->cl_auth = save_auth; + + if (rpc_createerr.cf_stat == 0) + rpc_createerr.cf_stat = RPC_AUTHERROR; + + return auth; +} + +/* + * Function: marshall_new_creds + * + * Purpose: (pre-)serialize auth_msg and client_handle fields of + * auth_gssapi_creds into auth->cred_buf + * + * Arguments: + * + * auth (r/w) the AUTH structure to modify + * auth_msg (r) the auth_msg field to serialize + * client_handle (r) the client_handle field to serialize, or + * NULL + * + * Returns: TRUE if successful, FALSE if not + * + * Requires: auth must point to a valid GSS-API auth structure, auth_msg + * must be TRUE or FALSE, client_handle must be a gss_buffer_t with a valid + * value and length field or NULL. + * + * Effects: auth->ah_cred is set to the serialized auth_gssapi_creds + * version 2 structure (stored in the cred_buf field of private data) + * containing version, auth_msg and client_handle. + * auth->ah_cred.oa_flavor is set to AUTH_GSSAPI. If cliend_handle is + * NULL, it is treated as if it had a length of 0 and a value of NULL. + * + * Modifies: auth + */ +static bool_t marshall_new_creds(auth, auth_msg, client_handle) + AUTH *auth; + bool_t auth_msg; + gss_buffer_t client_handle; +{ + auth_gssapi_creds creds; + XDR xdrs; + + PRINTF(("marshall_new_creds: starting\n")); + + creds.version = 2; + + creds.auth_msg = auth_msg; + if (client_handle) + GSS_COPY_BUFFER(creds.client_handle, *client_handle) + else { + creds.client_handle.length = 0; + creds.client_handle.value = NULL; + } + + xdrmem_create(&xdrs, AUTH_PRIVATE(auth)->cred_buf, + MAX_AUTH_BYTES, XDR_ENCODE); + if (! xdr_authgssapi_creds(&xdrs, &creds)) { + PRINTF(("marshall_new_creds: failed encoding auth_gssapi_creds\n")); + XDR_DESTROY(&xdrs); + return FALSE; + } + AUTH_PRIVATE(auth)->cred_len = xdr_getpos(&xdrs); + XDR_DESTROY(&xdrs); + + PRINTF(("marshall_new_creds: auth_gssapi_creds is %d bytes\n", + AUTH_PRIVATE(auth)->cred_len)); + + auth->ah_cred.oa_flavor = AUTH_GSSAPI; + auth->ah_cred.oa_base = (char *) AUTH_PRIVATE(auth)->cred_buf; + auth->ah_cred.oa_length = AUTH_PRIVATE(auth)->cred_len; + + PRINTF(("marshall_new_creds: succeeding\n")); + + return TRUE; +} + + +/* + * Function: auth_gssapi_nextverf + * + * Purpose: None. + * + * Effects: None. Never called. + */ +static void auth_gssapi_nextverf(/*auth*/) + /*AUTH *auth;*/ +{ +} + +/* + * Function: auth_gssapi_marhsall + * + * Purpose: Marshall RPC credentials and verifier onto xdr stream. + * + * Arguments: + * + * auth (r/w) AUTH structure for client + * xdrs (r/w) XDR stream to marshall to + * + * Returns: boolean indicating success/failure + * + * Effects: + * + * The pre-serialized credentials in cred_buf are serialized. If the + * context is established, the sealed sequence number is serialized as + * the verifier. If the context is not established, an empty verifier + * is serialized. The sequence number is *not* incremented, because + * this function is called multiple times if retransmission is required. + * + * If this took all the header fields as arguments, it could sign + * them. + */ +static bool_t auth_gssapi_marshall(auth, xdrs) + AUTH *auth; + XDR *xdrs; +{ + OM_uint32 minor_stat; + gss_buffer_desc out_buf; + rpc_u_int32 seq_num; + + if (AUTH_PRIVATE(auth)->established == TRUE) { + PRINTF(("gssapi_marshall: starting\n")); + + seq_num = AUTH_PRIVATE(auth)->seq_num + 1; + + PRINTF(("gssapi_marshall: sending seq_num %d\n", seq_num)); + + if (auth_gssapi_seal_seq(AUTH_PRIVATE(auth)->context, seq_num, + &out_buf) == FALSE) { + PRINTF(("gssapi_marhshall: seal failed\n")); + } + + auth->ah_verf.oa_base = out_buf.value; + auth->ah_verf.oa_length = out_buf.length; + + if (! xdr_opaque_auth(xdrs, &auth->ah_cred) || + ! xdr_opaque_auth(xdrs, &auth->ah_verf)) { + (void) gss_release_buffer(&minor_stat, &out_buf); + return FALSE; + } + (void) gss_release_buffer(&minor_stat, &out_buf); + } else { + PRINTF(("gssapi_marshall: not established, sending null verf\n")); + + auth->ah_verf.oa_base = NULL; + auth->ah_verf.oa_length = 0; + + if (! xdr_opaque_auth(xdrs, &auth->ah_cred) || + ! xdr_opaque_auth(xdrs, &auth->ah_verf)) { + return FALSE; + } + } + + return TRUE; +} + +/* + * Function: auth_gssapi_validate + * + * Purpose: Validate RPC response verifier from server. + * + * Effects: See design document, section XXX. + */ +static bool_t auth_gssapi_validate(auth, verf) + AUTH *auth; + struct opaque_auth *verf; +{ + gss_buffer_desc in_buf; + rpc_u_int32 seq_num; + + if (AUTH_PRIVATE(auth)->established == FALSE) { + PRINTF(("gssapi_validate: not established, noop\n")); + return TRUE; + } + + PRINTF(("gssapi_validate: starting\n")); + + in_buf.length = verf->oa_length; + in_buf.value = verf->oa_base; + if (auth_gssapi_unseal_seq(AUTH_PRIVATE(auth)->context, &in_buf, + &seq_num) == FALSE) { + PRINTF(("gssapi_validate: failed unsealing verifier\n")); + return FALSE; + } + + /* we sent seq_num+1, so we should get back seq_num+2 */ + if (AUTH_PRIVATE(auth)->seq_num+2 != seq_num) { + PRINTF(("gssapi_validate: expecting seq_num %d, got %d (%#x)\n", + AUTH_PRIVATE(auth)->seq_num + 2, seq_num, seq_num)); + return FALSE; + } + PRINTF(("gssapi_validate: seq_num %d okay\n", seq_num)); + + /* +1 for successful transmission, +1 for successful validation */ + AUTH_PRIVATE(auth)->seq_num += 2; + + PRINTF(("gssapi_validate: succeeding\n")); + + return TRUE; +} + +/* + * Function: auth_gssapi_refresh + * + * Purpose: Attempts to resyncrhonize the sequence number. + * + * Effects: + * + * When the server receives a properly authenticated RPC call, it + * increments the sequence number it is expecting from the client. + * But if the server's response is lost for any reason, the client + * can't know whether the server ever received it, assumes it didn't, + * and does *not* increment its sequence number. Thus, the client's + * next call will fail with AUTH_REJECTEDCRED because the server will + * think it is a replay attack. + * + * When an AUTH_REJECTEDCRED error arrives, this function attempts to + * resyncrhonize by incrementing the client's sequence number and + * returning TRUE. If any other error arrives, it returns FALSE. + */ +static bool_t auth_gssapi_refresh(auth, msg) + AUTH *auth; + struct rpc_msg *msg; +{ + if (msg->rm_reply.rp_rjct.rj_stat == AUTH_ERROR && + msg->rm_reply.rp_rjct.rj_why == AUTH_REJECTEDVERF) { + PRINTF(("gssapi_refresh: rejected verifier, incrementing\n")); + AUTH_PRIVATE(auth)->seq_num++; + return TRUE; + } else { + PRINTF(("gssapi_refresh: failing\n")); + return FALSE; + } +} + +/* + * Function: auth_gssapi_destroy + * + * Purpose: Destroy a GSS-API authentication structure. + * + * Effects: This function destroys the GSS-API authentication + * context, and sends a message to the server instructing it to + * invokte gss_process_token() and thereby destroy its corresponding + * context. Since the client doesn't really care whether the server + * gets this message, no failures are reported. + */ +static void auth_gssapi_destroy(auth) + AUTH *auth; +{ + struct timeval timeout; + OM_uint32 gssstat, minor_stat; + gss_cred_id_t cred; + int callstat; + + if (AUTH_PRIVATE(auth)->client_handle.length == 0) { + PRINTF(("gssapi_destroy: no client_handle, not calling destroy\n")); + goto skip_call; + } + + PRINTF(("gssapi_destroy: marshalling new creds\n")); + if (!marshall_new_creds(auth, TRUE, &AUTH_PRIVATE(auth)->client_handle)) { + PRINTF(("gssapi_destroy: marshall_new_creds failed\n")); + goto skip_call; + } + + PRINTF(("gssapi_destroy: calling GSSAPI_DESTROY\n")); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + callstat = clnt_call(AUTH_PRIVATE(auth)->clnt, AUTH_GSSAPI_DESTROY, + xdr_void, NULL, xdr_void, NULL, timeout); + if (callstat != RPC_SUCCESS) + clnt_sperror(AUTH_PRIVATE(auth)->clnt, + "gssapi_destroy: GSSAPI_DESTROY failed"); + +skip_call: + PRINTF(("gssapi_destroy: deleting context\n")); + gssstat = gss_delete_sec_context(&minor_stat, + &AUTH_PRIVATE(auth)->context, + NULL); + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat, + minor_stat)); + if (AUTH_PRIVATE(auth)->def_cred) { + cred = GSS_C_NO_CREDENTIAL; + gssstat = gss_release_cred(&minor_stat, &cred); + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("deleting default credential", + gssstat, minor_stat)); + } + + if (AUTH_PRIVATE(auth)->client_handle.length != 0) + gss_release_buffer(&minor_stat, + &AUTH_PRIVATE(auth)->client_handle); + +#if 0 + PRINTF(("gssapi_destroy: calling GSSAPI_EXIT\n")); + AUTH_PRIVATE(auth)->established = FALSE; + callstat = clnt_call(AUTH_PRIVATE(auth)->clnt, AUTH_GSSAPI_EXIT, + xdr_void, NULL, xdr_void, NULL, timeout); +#endif + + free(auth->ah_private); + free(auth); + PRINTF(("gssapi_destroy: done\n")); +} + +/* + * Function: auth_gssapi_wrap + * + * Purpose: encrypt the serialized arguments from xdr_func applied to + * xdr_ptr and write the result to xdrs. + * + * Effects: See design doc, section XXX. + */ +static bool_t auth_gssapi_wrap(auth, out_xdrs, xdr_func, xdr_ptr) + AUTH *auth; + XDR *out_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + OM_uint32 gssstat, minor_stat; + + if (! AUTH_PRIVATE(auth)->established) { + PRINTF(("gssapi_wrap: context not established, noop\n")); + return (*xdr_func)(out_xdrs, xdr_ptr); + } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, + AUTH_PRIVATE(auth)->context, + AUTH_PRIVATE(auth)->seq_num+1, + out_xdrs, xdr_func, xdr_ptr)) { + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments", + gssstat, minor_stat)); + return FALSE; + } else + return TRUE; +} + +/* + * Function: auth_gssapi_unwrap + * + * Purpose: read encrypted arguments from xdrs, decrypt, and + * deserialize with xdr_func into xdr_ptr. + * + * Effects: See design doc, section XXX. + */ +static bool_t auth_gssapi_unwrap(auth, in_xdrs, xdr_func, xdr_ptr) + AUTH *auth; + XDR *in_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + OM_uint32 gssstat, minor_stat; + + if (! AUTH_PRIVATE(auth)->established) { + PRINTF(("gssapi_unwrap: context not established, noop\n")); + return (*xdr_func)(in_xdrs, xdr_ptr); + } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, + AUTH_PRIVATE(auth)->context, + AUTH_PRIVATE(auth)->seq_num, + in_xdrs, xdr_func, xdr_ptr)) { + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments", + gssstat, minor_stat)); + return FALSE; + } else + return TRUE; +} diff --git a/src/lib/rpc/auth_gssapi.h b/src/lib/rpc/auth_gssapi.h new file mode 100644 index 000000000..9092149a5 --- /dev/null +++ b/src/lib/rpc/auth_gssapi.h @@ -0,0 +1,161 @@ +/* + * auth_gssapi.h, Protocol for GSS-API style authentication parameters for RPC + * + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.18 1996/07/22 20:39:41 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.17.4.1 1996/07/18 04:18:31 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.17.2.1 1996/06/20 23:35:44 marc + * File added to the repository on a branch + * + * Revision 1.17 1996/05/12 06:11:38 marc + * renamed lots of types: u_foo to unsigned foo, and foo32 to rpc_foo32. This is to make autoconfiscation less painful. + * + * Revision 1.16 1996/01/31 19:16:16 grier + * [secure/3570] + * Remove (void *) casts to memcpy() args + * + * Revision 1.15 1995/12/28 17:54:34 jik + * Don't define DEBUG_GSSAPI here. + * + * Revision 1.14 1995/12/13 14:03:01 grier + * Longs to ints for Alpha + * + * Revision 1.13 1995/11/07 23:15:26 grier + * memcpy() casts + * + * Revision 1.12 1995/05/25 18:35:59 bjaspan + * [secure-rpc/3103] log misc errors from RPC + * + * Revision 1.11 1994/10/27 12:39:14 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.11 1994/10/26 20:04:00 bjaspan + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.10 1993/11/12 02:32:50 bjaspan + * add badauth, don't use const_gss_OID + * + * Revision 1.9 1993/11/03 23:46:15 bjaspan + * new log_badverf format + * + * Revision 1.8 1993/11/03 21:21:38 bjaspan + * added log_badverf + * + * Revision 1.7 1993/11/03 01:29:56 bjaspan + * add const to gss_nt_* + * + */ + +#define AUTH_GSSAPI_EXIT 0 +#define AUTH_GSSAPI_INIT 1 +#define AUTH_GSSAPI_CONTINUE_INIT 2 +#define AUTH_GSSAPI_MSG 3 +#define AUTH_GSSAPI_DESTROY 4 + +typedef struct _auth_gssapi_name { + char *name; + gss_OID type; +} auth_gssapi_name; + +typedef struct _auth_gssapi_creds { + rpc_u_int32 version; + bool_t auth_msg; + gss_buffer_desc client_handle; +} auth_gssapi_creds; + +typedef struct _auth_gssapi_init_arg { + rpc_u_int32 version; + gss_buffer_desc token; +} auth_gssapi_init_arg; + +typedef struct _auth_gssapi_init_res { + rpc_u_int32 version; + gss_buffer_desc client_handle; + OM_uint32 gss_major, gss_minor; + gss_buffer_desc token; + gss_buffer_desc signed_isn; +} auth_gssapi_init_res; + +typedef void (*auth_gssapi_log_badauth_func)(OM_uint32 major, + OM_uint32 minor, + struct sockaddr_in *raddr, + caddr_t data); + +typedef void (*auth_gssapi_log_badverf_func)(gss_name_t client, + gss_name_t server, + struct svc_req *rqst, + struct rpc_msg *msg, + caddr_t data); + +typedef void (*auth_gssapi_log_miscerr_func)(struct svc_req *rqst, + struct rpc_msg *msg, + char *error, + caddr_t data); + +bool_t xdr_authgssapi_creds(); +bool_t xdr_authgssapi_init_arg(); +bool_t xdr_authgssapi_init_res(); + +bool_t auth_gssapi_wrap_data(OM_uint32 *major, OM_uint32 *minor, + gss_ctx_id_t context, rpc_u_int32 seq_num, XDR + *out_xdrs, bool_t (*xdr_func)(), caddr_t + xdr_ptr); +bool_t auth_gssapi_unwrap_data(OM_uint32 *major, OM_uint32 *minor, + gss_ctx_id_t context, rpc_u_int32 seq_num, XDR + *in_xdrs, bool_t (*xdr_func)(), caddr_t + xdr_ptr); + +AUTH *auth_gssapi_create(CLIENT *clnt, + OM_uint32 *major_status, + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_name_t target_name, + gss_OID mech_type, + int req_flags, + OM_uint32 time_req, + gss_OID *actual_mech_type, + int *ret_flags, + OM_uint32 *time_rec); + +AUTH *auth_gssapi_create_default(CLIENT *clnt, char *service_name); + +void auth_gssapi_display_status(char *msg, OM_uint32 major, + OM_uint32 minor); +bool_t _svcauth_gssapi_set_name(char *name, gss_OID name_type); + +void _svcauth_set_log_badauth_func(auth_gssapi_log_badauth_func func, + caddr_t data); +void _svcauth_set_log_badverf_func(auth_gssapi_log_badverf_func func, + caddr_t data); +void _svcauth_set_log_miscerr_func(auth_gssapi_log_miscerr_func func, + caddr_t data); + +#define GSS_COPY_BUFFER(dest, src) { \ + (dest).length = (src).length; \ + (dest).value = (src).value; } + +#define GSS_DUP_BUFFER(dest, src) { \ + (dest).length = (src).length; \ + (dest).value = (void *) malloc((dest).length); \ + memcpy((dest).value, (src).value, (dest).length); } + +#define GSS_BUFFERS_EQUAL(b1, b2) (((b1).length == (b2).length) && \ + !memcmp((b1).value,(b2).value,(b1.length))) + diff --git a/src/lib/rpc/auth_gssapi_misc.c b/src/lib/rpc/auth_gssapi_misc.c new file mode 100644 index 000000000..bd7b6e5a3 --- /dev/null +++ b/src/lib/rpc/auth_gssapi_misc.c @@ -0,0 +1,390 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * $Log$ + * Revision 1.14 1996/07/22 20:39:44 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.13.4.1 1996/07/18 04:18:32 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.13.2.1 1996/06/20 23:35:49 marc + * File added to the repository on a branch + * + * Revision 1.13 1996/05/12 06:11:38 marc + * renamed lots of types: u_foo to unsigned foo, and foo32 to rpc_foo32. This is to make autoconfiscation less painful. + * + * Revision 1.12 1996/02/25 15:53:57 grier + * [secure/3570] + * OSF1 long changes + * + * Revision 1.11 1995/12/13 14:03:30 grier + * Longs to ints for Alpha + * + * Revision 1.10 1994/10/27 12:39:23 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] back out the backwards compat hack (I only put it + * there in case we ever decide we want it, which we won't) and just + * handle the version field as if it were always there + * + * [secure-rpc/2808] add a backwards-compatible version field encoder + * + * change to use GSS_ERROR &c macros; I don't think this is correct + * ============================================================================= + * + * Revision 1.12 1994/10/26 19:48:45 bjaspan + * [secure-rpc/2808] back out the backwards compat hack (I only put it + * there in case we ever decide we want it, which we won't) and just + * handle the version field as if it were always there + * + * Revision 1.11 1994/10/26 19:47:42 bjaspan + * [secure-rpc/2808] add a backwards-compatible version field encoder + * + * Revision 1.10 1993/12/19 22:19:40 bjaspan + * [secure-rpc/1077] use free instead of xdr_free(xdr_bytes,...) because + * xdr_bytes takes non-standard arguments + * + * Revision 1.9 1993/12/08 21:43:37 bjaspan + * use AUTH_GSSAPI_DISPLAY_STATUS macro, reindent + * + * Revision 1.8 1993/12/06 21:21:21 bjaspan + * debugging levels + * + * Revision 1.7 1993/11/03 23:46:34 bjaspan + * add debugging printfs showing amount of data wrapped/unwrapped + * + * Revision 1.6 1993/11/03 21:21:53 bjaspan + * unstatic misc_debug_gssapi + * + * Revision 1.5 1993/11/01 19:55:54 bjaspan + * improve display_status messages, and include gss_{major,minor} + * + * Revision 1.4 1993/10/28 22:08:21 bjaspan + * wrap/unwrap_data include sequence number in arg/result block + * + * Revision 1.3 1993/10/27 18:26:16 bjaspan + * use xdr_free instead of free(in_buf.value); doesn't actually make a + * difference, though + * + * Revision 1.2 1993/10/26 21:12:38 bjaspan + * cleaning + * + * Revision 1.1 1993/10/19 03:12:28 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include + +#include +#include + +#ifdef __CODECENTER__ +#define DEBUG_GSSAPI 1 +#endif + +#ifdef DEBUG_GSSAPI +int misc_debug_gssapi = DEBUG_GSSAPI; +#define L_PRINTF(l,args) if (misc_debug_gssapi >= l) printf args +#define PRINTF(args) L_PRINTF(99, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) \ + if (misc_debug_gssapi) auth_gssapi_display_status args +#else +#define PRINTF(args) +#define L_PRINTF(l, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) +#endif + +static void auth_gssapi_display_status_1(char *, OM_uint32, int, int); + +bool_t xdr_gss_buf(xdrs, buf) + XDR *xdrs; + gss_buffer_t buf; +{ + /* + * On decode, xdr_bytes will only allocate buf->value if the + * length read in is < maxsize (last arg). This is dumb, because + * the whole point of allocating memory is so that I don't *have* + * to know the maximum length. -1 effectively disables this + * braindamage. + */ + return xdr_bytes(xdrs, (char **) &buf->value, (unsigned int *) &buf->length, + (xdrs->x_op == XDR_DECODE && buf->value == NULL) + ? (unsigned int) -1 : (unsigned int) buf->length); +} + +bool_t xdr_authgssapi_creds(xdrs, creds) + XDR *xdrs; + auth_gssapi_creds *creds; +{ + if (! xdr_u_int32(xdrs, &creds->version) || + ! xdr_bool(xdrs, &creds->auth_msg) || + ! xdr_gss_buf(xdrs, &creds->client_handle)) + return FALSE; + return TRUE; +} + +bool_t xdr_authgssapi_init_arg(xdrs, init_arg) + XDR *xdrs; + auth_gssapi_init_arg *init_arg; +{ + if (! xdr_u_int32(xdrs, &init_arg->version) || + ! xdr_gss_buf(xdrs, &init_arg->token)) + return FALSE; + return TRUE; +} + +bool_t xdr_authgssapi_init_res(xdrs, init_res) + XDR *xdrs; + auth_gssapi_init_res *init_res; +{ + if (! xdr_u_int32(xdrs, &init_res->version) || + ! xdr_gss_buf(xdrs, &init_res->client_handle) || + ! xdr_u_int32(xdrs, &init_res->gss_major) || + ! xdr_u_int32(xdrs, &init_res->gss_minor) || + ! xdr_gss_buf(xdrs, &init_res->token) || + ! xdr_gss_buf(xdrs, &init_res->signed_isn)) + return FALSE; + return TRUE; +} + +bool_t auth_gssapi_seal_seq(context, seq_num, out_buf) + gss_ctx_id_t context; + rpc_u_int32 seq_num; + gss_buffer_t out_buf; +{ + gss_buffer_desc in_buf; + OM_uint32 gssstat, minor_stat; + rpc_u_int32 nl_seq_num; + + nl_seq_num = htonl(seq_num); + + in_buf.length = sizeof(rpc_u_int32); + in_buf.value = (char *) &nl_seq_num; + gssstat = gss_seal(&minor_stat, context, 0, GSS_C_QOP_DEFAULT, + &in_buf, NULL, out_buf); + if (gssstat != GSS_S_COMPLETE) { + PRINTF(("gssapi_seal_seq: failed\n")); + AUTH_GSSAPI_DISPLAY_STATUS(("sealing sequence number", + gssstat, minor_stat)); + return FALSE; + } + return TRUE; +} + +bool_t auth_gssapi_unseal_seq(context, in_buf, seq_num) + gss_ctx_id_t context; + gss_buffer_t in_buf; + rpc_u_int32 *seq_num; +{ + gss_buffer_desc out_buf; + OM_uint32 gssstat, minor_stat; + rpc_u_int32 nl_seq_num; + + gssstat = gss_unseal(&minor_stat, context, in_buf, &out_buf, + NULL, NULL); + if (gssstat != GSS_S_COMPLETE) { + PRINTF(("gssapi_unseal_seq: failed\n")); + AUTH_GSSAPI_DISPLAY_STATUS(("unsealing sequence number", + gssstat, minor_stat)); + return FALSE; + } else if (out_buf.length != sizeof(rpc_u_int32)) { + PRINTF(("gssapi_unseal_seq: unseal gave %d bytes\n", + out_buf.length)); + gss_release_buffer(&minor_stat, &out_buf); + return FALSE; + } + + nl_seq_num = *((rpc_u_int32 *) out_buf.value); + *seq_num = (rpc_u_int32) ntohl(nl_seq_num); + gss_release_buffer(&minor_stat, &out_buf); + + return TRUE; +} + +void auth_gssapi_display_status(char *msg, OM_uint32 major, OM_uint32 minor) +{ + auth_gssapi_display_status_1(msg, major, GSS_C_GSS_CODE, 0); + auth_gssapi_display_status_1(msg, minor, GSS_C_MECH_CODE, 0); +} + +static void auth_gssapi_display_status_1(char *m, OM_uint32 code, int type, + int rec) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc msg; + int msg_ctx; + + msg_ctx = 0; + while (1) { + gssstat = gss_display_status(&minor_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (gssstat != GSS_S_COMPLETE) { + if (!rec) { + auth_gssapi_display_status_1(m,gssstat,GSS_C_GSS_CODE,1); + auth_gssapi_display_status_1(m, minor_stat, + GSS_C_MECH_CODE, 1); + } else + fprintf(stderr,"GSS-API authentication error %s: " + "recursive failure!\n", msg); + return; + } + + fprintf(stderr, "GSS-API authentication error %s: %s\n", m, + (char *)msg.value); + (void) gss_release_buffer(&minor_stat, &msg); + + if (!msg_ctx) + break; + } +} + +bool_t auth_gssapi_wrap_data(major, minor, context, seq_num, out_xdrs, + xdr_func, xdr_ptr) + OM_uint32 *major, *minor; + gss_ctx_id_t context; + rpc_u_int32 seq_num; + XDR *out_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + gss_buffer_desc in_buf, out_buf; + XDR temp_xdrs; + int conf_state; + + PRINTF(("gssapi_wrap_data: starting\n")); + + *major = GSS_S_COMPLETE; + *minor = 0; /* assumption */ + + xdralloc_create(&temp_xdrs, XDR_ENCODE); + + /* serialize the sequence number into local memory */ + PRINTF(("gssapi_wrap_data: encoding seq_num %d\n", seq_num)); + if (! xdr_u_int32(&temp_xdrs, &seq_num)) { + PRINTF(("gssapi_wrap_data: serializing seq_num failed\n")); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + + /* serialize the arguments into local memory */ + if (!(*xdr_func)(&temp_xdrs, xdr_ptr)) { + PRINTF(("gssapi_wrap_data: serializing arguments failed\n")); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + + in_buf.length = xdr_getpos(&temp_xdrs); + in_buf.value = xdralloc_getdata(&temp_xdrs); + + *major = gss_seal(minor, context, 1, + GSS_C_QOP_DEFAULT, &in_buf, &conf_state, + &out_buf); + if (*major != GSS_S_COMPLETE) { + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + + PRINTF(("gssapi_wrap_data: %d bytes data, %d bytes sealed\n", + in_buf.length, out_buf.length)); + + /* write the token */ + if (! xdr_bytes(out_xdrs, (char **) &out_buf.value, + (unsigned int *) &out_buf.length, + out_buf.length)) { + PRINTF(("gssapi_wrap_data: serializing encrypted data failed\n")); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + + *major = gss_release_buffer(minor, &out_buf); + + PRINTF(("gssapi_wrap_data: succeeding\n\n")); + XDR_DESTROY(&temp_xdrs); + return TRUE; +} + +bool_t auth_gssapi_unwrap_data(major, minor, context, seq_num, + in_xdrs, xdr_func, xdr_ptr) + OM_uint32 *major, *minor; + gss_ctx_id_t context; + rpc_u_int32 seq_num; + XDR *in_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + gss_buffer_desc in_buf, out_buf; + XDR temp_xdrs; + rpc_u_int32 verf_seq_num; + int conf, qop; + + PRINTF(("gssapi_unwrap_data: starting\n")); + + *major = GSS_S_COMPLETE; + *minor = 0; /* assumption */ + + in_buf.value = NULL; + out_buf.value = NULL; + + if (! xdr_bytes(in_xdrs, (char **) &in_buf.value, + (unsigned int *) &in_buf.length, (unsigned int) -1)) { + PRINTF(("gssapi_unwrap_data: deserializing encrypted data failed\n")); + return FALSE; + } + + *major = gss_unseal(minor, context, &in_buf, &out_buf, &conf, + &qop); + free(in_buf.value); + if (*major != GSS_S_COMPLETE) + return FALSE; + + PRINTF(("gssapi_unwrap_data: %d bytes data, %d bytes sealed\n", + out_buf.length, in_buf.length)); + + xdrmem_create(&temp_xdrs, out_buf.value, out_buf.length, XDR_DECODE); + + /* deserialize the sequence number */ + if (! xdr_u_int32(&temp_xdrs, &verf_seq_num)) { + PRINTF(("gssapi_unwrap_data: deserializing verf_seq_num failed\n")); + gss_release_buffer(minor, &out_buf); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + if (verf_seq_num != seq_num) { + PRINTF(("gssapi_unwrap_data: seq %d specified, read %d\n", + seq_num, verf_seq_num)); + gss_release_buffer(minor, &out_buf); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + PRINTF(("gssapi_unwrap_data: unwrap seq_num %d okay\n", verf_seq_num)); + + /* deserialize the arguments into xdr_ptr */ + if (! (*xdr_func)(&temp_xdrs, xdr_ptr)) { + PRINTF(("gssapi_unwrap_data: deserializing arguments failed\n")); + gss_release_buffer(minor, &out_buf); + XDR_DESTROY(&temp_xdrs); + return FALSE; + } + + PRINTF(("gssapi_unwrap_data: succeeding\n\n")); + + gss_release_buffer(minor, &out_buf); + XDR_DESTROY(&temp_xdrs); + return TRUE; +} diff --git a/src/lib/rpc/auth_none.c b/src/lib/rpc/auth_none.c new file mode 100644 index 000000000..90f3bb8be --- /dev/null +++ b/src/lib/rpc/auth_none.c @@ -0,0 +1,136 @@ +/* @(#)auth_none.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)auth_none.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * auth_none.c + * Creates a client authentication handle for passing "null" + * credentials and verifiers to remote systems. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#define MAX_MARSHEL_SIZE 20 + +/* + * Authenticator operations routines + */ +static void authnone_verf(); +static void authnone_destroy(); +static bool_t authnone_marshal(); +static bool_t authnone_validate(); +static bool_t authnone_refresh(); + +static struct auth_ops ops = { + authnone_verf, + authnone_marshal, + authnone_validate, + authnone_refresh, + authnone_destroy, + authany_wrap, + authany_wrap, +}; + +static struct authnone_private { + AUTH no_client; + char marshalled_client[MAX_MARSHEL_SIZE]; + unsigned int mcnt; +} *authnone_private; + +AUTH * +authnone_create() +{ + register struct authnone_private *ap = authnone_private; + XDR xdr_stream; + register XDR *xdrs; + + if (ap == 0) { + ap = (struct authnone_private *)calloc(1, sizeof (*ap)); + if (ap == 0) + return (0); + authnone_private = ap; + } + if (!ap->mcnt) { + ap->no_client.ah_cred = ap->no_client.ah_verf = _null_auth; + ap->no_client.ah_ops = &ops; + xdrs = &xdr_stream; + xdrmem_create(xdrs, ap->marshalled_client, (unsigned int)MAX_MARSHEL_SIZE, + XDR_ENCODE); + (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_cred); + (void)xdr_opaque_auth(xdrs, &ap->no_client.ah_verf); + ap->mcnt = XDR_GETPOS(xdrs); + XDR_DESTROY(xdrs); + } + return (&ap->no_client); +} + +/*ARGSUSED*/ +static bool_t +authnone_marshal(client, xdrs) + AUTH *client; + XDR *xdrs; +{ + register struct authnone_private *ap = authnone_private; + + if (ap == 0) + return (0); + return ((*xdrs->x_ops->x_putbytes)(xdrs, + ap->marshalled_client, ap->mcnt)); +} + +static void +authnone_verf() +{ +} + +static bool_t +authnone_validate() +{ + + return (TRUE); +} + +static bool_t +authnone_refresh() +{ + + return (FALSE); +} + +static void +authnone_destroy() +{ +} diff --git a/src/lib/rpc/auth_unix.c b/src/lib/rpc/auth_unix.c new file mode 100644 index 000000000..b41e442fc --- /dev/null +++ b/src/lib/rpc/auth_unix.c @@ -0,0 +1,322 @@ +/* @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * auth_unix.c, Implements UNIX style authentication parameters. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The system is very weak. The client uses no encryption for it's + * credentials and only sends null verifiers. The server sends backs + * null verifiers or optionally a verifier that suggests a new short hand + * for the credentials. + * + */ + +#include + +#include +#include +#include +#include + + +/* + * Unix authenticator operations vector + */ +static void authunix_nextverf(); +static bool_t authunix_marshal(); +static bool_t authunix_validate(); +static bool_t authunix_refresh(); +static void authunix_destroy(); + +static struct auth_ops auth_unix_ops = { + authunix_nextverf, + authunix_marshal, + authunix_validate, + authunix_refresh, + authunix_destroy, + authany_wrap, + authany_wrap, +}; + +/* + * This struct is pointed to by the ah_private field of an auth_handle. + */ +struct audata { + struct opaque_auth au_origcred; /* original credentials */ + struct opaque_auth au_shcred; /* short hand cred */ + rpc_u_int32 au_shfaults; /* short hand cache faults */ + char au_marshed[MAX_AUTH_BYTES]; + unsigned int au_mpos; /* xdr pos at end of marshed */ +}; +#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private) + +static bool_t marshal_new_auth(); + + +/* + * Create a unix style authenticator. + * Returns an auth handle with the given stuff in it. + */ +AUTH * +authunix_create(machname, uid, gid, len, aup_gids) + char *machname; + int uid; + int gid; + register int len; + int *aup_gids; +{ + struct authunix_parms aup; + char mymem[MAX_AUTH_BYTES]; + struct timeval now; + XDR xdrs; + register AUTH *auth; + register struct audata *au; + + /* + * Allocate and set up auth handle + */ + auth = (AUTH *)mem_alloc(sizeof(*auth)); +#ifndef KERNEL + if (auth == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + au = (struct audata *)mem_alloc(sizeof(*au)); +#ifndef KERNEL + if (au == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + auth->ah_ops = &auth_unix_ops; + auth->ah_private = (caddr_t)au; + auth->ah_verf = au->au_shcred = _null_auth; + au->au_shfaults = 0; + + /* + * fill in param struct from the given params + */ + (void)gettimeofday(&now, (struct timezone *)0); + aup.aup_time = now.tv_sec; + aup.aup_machname = machname; + aup.aup_uid = uid; + aup.aup_gid = gid; + aup.aup_len = (unsigned int)len; + aup.aup_gids = aup_gids; + + /* + * Serialize the parameters into origcred + */ + xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE); + if (! xdr_authunix_parms(&xdrs, &aup)) + abort(); + au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs); + au->au_origcred.oa_flavor = AUTH_UNIX; +#ifdef KERNEL + au->au_origcred.oa_base = mem_alloc((unsigned int) len); +#else + if ((au->au_origcred.oa_base = mem_alloc((unsigned int) len)) == NULL) { + (void)fprintf(stderr, "authunix_create: out of memory\n"); + return (NULL); + } +#endif + memmove(au->au_origcred.oa_base, mymem, (unsigned int)len); + + /* + * set auth handle to reflect new cred. + */ + auth->ah_cred = au->au_origcred; + marshal_new_auth(auth); + return (auth); +} + +/* + * Returns an auth handle with parameters determined by doing lots of + * syscalls. + */ +AUTH * +authunix_create_default() +{ + register int len; + char machname[MAX_MACHINE_NAME + 1]; + register int uid; + register int gid; + int gids[NGRPS]; + + if (gethostname(machname, MAX_MACHINE_NAME) == -1) + abort(); + machname[MAX_MACHINE_NAME] = 0; + uid = geteuid(); + gid = getegid(); + if ((len = getgroups(NGRPS, gids)) < 0) + abort(); + return (authunix_create(machname, uid, gid, len, gids)); +} + +/* + * authunix operations + */ + +static void +authunix_nextverf(auth) + AUTH *auth; +{ + /* no action necessary */ +} + +static bool_t +authunix_marshal(auth, xdrs) + AUTH *auth; + XDR *xdrs; +{ + register struct audata *au = AUTH_PRIVATE(auth); + + return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos)); +} + +static bool_t +authunix_validate(auth, verf) + register AUTH *auth; + struct opaque_auth verf; +{ + register struct audata *au; + XDR xdrs; + + if (verf.oa_flavor == AUTH_SHORT) { + au = AUTH_PRIVATE(auth); + xdrmem_create(&xdrs, verf.oa_base, verf.oa_length, XDR_DECODE); + + if (au->au_shcred.oa_base != NULL) { + mem_free(au->au_shcred.oa_base, + au->au_shcred.oa_length); + au->au_shcred.oa_base = NULL; + } + if (xdr_opaque_auth(&xdrs, &au->au_shcred)) { + auth->ah_cred = au->au_shcred; + } else { + xdrs.x_op = XDR_FREE; + (void)xdr_opaque_auth(&xdrs, &au->au_shcred); + au->au_shcred.oa_base = NULL; + auth->ah_cred = au->au_origcred; + } + marshal_new_auth(auth); + } + return (TRUE); +} + +static bool_t +authunix_refresh(auth) + register AUTH *auth; +{ + register struct audata *au = AUTH_PRIVATE(auth); + struct authunix_parms aup; + struct timeval now; + XDR xdrs; + register int stat; + + if (auth->ah_cred.oa_base == au->au_origcred.oa_base) { + /* there is no hope. Punt */ + return (FALSE); + } + au->au_shfaults ++; + + /* first deserialize the creds back into a struct authunix_parms */ + aup.aup_machname = NULL; + aup.aup_gids = (int *)NULL; + xdrmem_create(&xdrs, au->au_origcred.oa_base, + au->au_origcred.oa_length, XDR_DECODE); + stat = xdr_authunix_parms(&xdrs, &aup); + if (! stat) + goto done; + + /* update the time and serialize in place */ + (void)gettimeofday(&now, (struct timezone *)0); + aup.aup_time = now.tv_sec; + xdrs.x_op = XDR_ENCODE; + XDR_SETPOS(&xdrs, 0); + stat = xdr_authunix_parms(&xdrs, &aup); + if (! stat) + goto done; + auth->ah_cred = au->au_origcred; + marshal_new_auth(auth); +done: + /* free the struct authunix_parms created by deserializing */ + xdrs.x_op = XDR_FREE; + (void)xdr_authunix_parms(&xdrs, &aup); + XDR_DESTROY(&xdrs); + return (stat); +} + +static void +authunix_destroy(auth) + register AUTH *auth; +{ + register struct audata *au = AUTH_PRIVATE(auth); + + mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length); + + if (au->au_shcred.oa_base != NULL) + mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length); + + mem_free(auth->ah_private, sizeof(struct audata)); + + if (auth->ah_verf.oa_base != NULL) + mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length); + + mem_free((caddr_t)auth, sizeof(*auth)); +} + +/* + * Marshals (pre-serializes) an auth struct. + * sets private data, au_marshed and au_mpos + */ +static bool_t +marshal_new_auth(auth) + register AUTH *auth; +{ + XDR xdr_stream; + register XDR *xdrs = &xdr_stream; + register struct audata *au = AUTH_PRIVATE(auth); + + xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE); + if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) || + (! xdr_opaque_auth(xdrs, &(auth->ah_verf)))) { + perror("auth_none.c - Fatal marshalling problem"); + } else { + au->au_mpos = XDR_GETPOS(xdrs); + } + XDR_DESTROY(xdrs); +} diff --git a/src/lib/rpc/auth_unix.h b/src/lib/rpc/auth_unix.h new file mode 100644 index 000000000..4b54f1db6 --- /dev/null +++ b/src/lib/rpc/auth_unix.h @@ -0,0 +1,72 @@ +/* @(#)auth_unix.h 2.2 88/07/29 4.0 RPCSRC; from 1.8 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)auth_unix.h 1.5 86/07/16 SMI */ + +/* + * auth_unix.h, Protocol for UNIX style authentication parameters for RPC + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +/* + * The system is very weak. The client uses no encryption for it + * credentials and only sends null verifiers. The server sends backs + * null verifiers or optionally a verifier that suggests a new short hand + * for the credentials. + */ + +/* The machine name is part of a credential; it may not exceed 255 bytes */ +#define MAX_MACHINE_NAME 255 + +/* gids compose part of a credential; there may not be more than 16 of them */ +#define NGRPS 16 + +/* + * Unix style credentials. + */ +struct authunix_parms { + rpc_u_int32 aup_time; + char *aup_machname; + int aup_uid; + int aup_gid; + unsigned int aup_len; + int *aup_gids; +}; + +extern bool_t xdr_authunix_parms(); + +/* + * If a response verifier has flavor AUTH_SHORT, + * then the body of the response verifier encapsulates the following structure; + * again it is serialized in the obvious fashion. + */ +struct short_hand_verf { + struct opaque_auth new_cred; +}; diff --git a/src/lib/rpc/authunix_prot.c b/src/lib/rpc/authunix_prot.c new file mode 100644 index 000000000..36770f298 --- /dev/null +++ b/src/lib/rpc/authunix_prot.c @@ -0,0 +1,66 @@ +/* @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * authunix_prot.c + * XDR for UNIX style authentication parameters for RPC + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + + +#include +#include +#include +#include + +/* + * XDR for unix authentication parameters. + */ +bool_t +xdr_authunix_parms(xdrs, p) + register XDR *xdrs; + register struct authunix_parms *p; +{ + + if (xdr_u_int32(xdrs, &(p->aup_time)) + && xdr_string(xdrs, &(p->aup_machname), MAX_MACHINE_NAME) + && xdr_int(xdrs, &(p->aup_uid)) + && xdr_int(xdrs, &(p->aup_gid)) + && xdr_array(xdrs, (caddr_t *)&(p->aup_gids), + &(p->aup_len), NGRPS, sizeof(int), xdr_int) ) { + return (TRUE); + } + return (FALSE); +} + diff --git a/src/lib/rpc/bindresvport.c b/src/lib/rpc/bindresvport.c new file mode 100644 index 000000000..875aeefb6 --- /dev/null +++ b/src/lib/rpc/bindresvport.c @@ -0,0 +1,79 @@ +static char sccsid[] = "@(#)bindresvport.c 2.2 88/07/29 4.0 RPCSRC 1.8 88/02/08 SMI"; +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1987 by Sun Microsystems, Inc. + */ + +#include +#include +#include +#include + +/* + * Bind a socket to a privileged IP port + */ +bindresvport(sd, sin) + int sd; + struct sockaddr_in *sin; +{ + int res; + static short port; + struct sockaddr_in myaddr; + extern int errno; + int i; + +#define STARTPORT 600 +#define ENDPORT (IPPORT_RESERVED - 1) +#define NPORTS (ENDPORT - STARTPORT + 1) + + if (sin == (struct sockaddr_in *)0) { + sin = &myaddr; + memset(sin, 0, sizeof (*sin)); + sin->sin_family = AF_INET; + } else if (sin->sin_family != AF_INET) { + errno = EPFNOSUPPORT; + return (-1); + } + if (port == 0) { + port = (getpid() % NPORTS) + STARTPORT; + } + res = -1; + errno = EADDRINUSE; + for (i = 0; i < NPORTS && res < 0 && errno == EADDRINUSE; i++) { + sin->sin_port = htons(port++); + if (port > ENDPORT) { + port = STARTPORT; + } + res = bind(sd, (struct sockaddr *) sin, + sizeof(struct sockaddr_in)); + } + return (res); +} diff --git a/src/lib/rpc/clnt.h b/src/lib/rpc/clnt.h new file mode 100644 index 000000000..442a96d44 --- /dev/null +++ b/src/lib/rpc/clnt.h @@ -0,0 +1,335 @@ +/* @(#)clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.31 88/02/08 SMI*/ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * clnt.h - Client side remote procedure call interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef _CLNT_ +#define _CLNT_ + +/* + * Rpc calls return an enum clnt_stat. This should be looked at more, + * since each implementation is required to live with this (implementation + * independent) list of errors. + */ +enum clnt_stat { + RPC_SUCCESS=0, /* call succeeded */ + /* + * local errors + */ + RPC_CANTENCODEARGS=1, /* can't encode arguments */ + RPC_CANTDECODERES=2, /* can't decode results */ + RPC_CANTSEND=3, /* failure in sending call */ + RPC_CANTRECV=4, /* failure in receiving result */ + RPC_TIMEDOUT=5, /* call timed out */ + /* + * remote errors + */ + RPC_VERSMISMATCH=6, /* rpc versions not compatible */ + RPC_AUTHERROR=7, /* authentication error */ + RPC_PROGUNAVAIL=8, /* program not available */ + RPC_PROGVERSMISMATCH=9, /* program version mismatched */ + RPC_PROCUNAVAIL=10, /* procedure unavailable */ + RPC_CANTDECODEARGS=11, /* decode arguments error */ + RPC_SYSTEMERROR=12, /* generic "other problem" */ + + /* + * callrpc & clnt_create errors + */ + RPC_UNKNOWNHOST=13, /* unknown host name */ + RPC_UNKNOWNPROTO=17, /* unkown protocol */ + + /* + * _ create errors + */ + RPC_PMAPFAILURE=14, /* the pmapper failed in its call */ + RPC_PROGNOTREGISTERED=15, /* remote program is not registered */ + /* + * unspecified error + */ + RPC_FAILED=16 +}; + + +/* + * Error info. + */ +struct rpc_err { + enum clnt_stat re_status; + union { + int RE_errno; /* realated system error */ + enum auth_stat RE_why; /* why the auth error occurred */ + struct { + rpc_u_int32 low; /* lowest verion supported */ + rpc_u_int32 high; /* highest verion supported */ + } RE_vers; + struct { /* maybe meaningful if RPC_FAILED */ + rpc_int32 s1; + rpc_int32 s2; + } RE_lb; /* life boot & debugging only */ + } ru; +#define re_errno ru.RE_errno +#define re_why ru.RE_why +#define re_vers ru.RE_vers +#define re_lb ru.RE_lb +}; + + +/* + * Client rpc handle. + * Created by individual implementations, see e.g. rpc_udp.c. + * Client is responsible for initializing auth, see e.g. auth_none.c. + */ +typedef struct { + AUTH *cl_auth; /* authenticator */ + struct clnt_ops { + enum clnt_stat (*cl_call)(); /* call remote procedure */ + void (*cl_abort)(); /* abort a call */ + void (*cl_geterr)(); /* get specific error code */ + bool_t (*cl_freeres)(); /* frees results */ + void (*cl_destroy)();/* destroy this structure */ + bool_t (*cl_control)();/* the ioctl() of rpc */ + } *cl_ops; + caddr_t cl_private; /* private stuff */ +} CLIENT; + + +/* + * client side rpc interface ops + * + * Parameter types are: + * + */ + +/* + * enum clnt_stat + * CLNT_CALL(rh, proc, xargs, argsp, xres, resp, timeout) + * CLIENT *rh; + * rpc_u_int32 proc; + * xdrproc_t xargs; + * caddr_t argsp; + * xdrproc_t xres; + * caddr_t resp; + * struct timeval timeout; + */ +#define CLNT_CALL(rh, proc, xargs, argsp, xres, resp, secs) \ + ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs)) +#define clnt_call(rh, proc, xargs, argsp, xres, resp, secs) \ + ((*(rh)->cl_ops->cl_call)(rh, proc, xargs, argsp, xres, resp, secs)) + +/* + * void + * CLNT_ABORT(rh); + * CLIENT *rh; + */ +#define CLNT_ABORT(rh) ((*(rh)->cl_ops->cl_abort)(rh)) +#define clnt_abort(rh) ((*(rh)->cl_ops->cl_abort)(rh)) + +/* + * struct rpc_err + * CLNT_GETERR(rh); + * CLIENT *rh; + */ +#define CLNT_GETERR(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp)) +#define clnt_geterr(rh,errp) ((*(rh)->cl_ops->cl_geterr)(rh, errp)) + + +/* + * bool_t + * CLNT_FREERES(rh, xres, resp); + * CLIENT *rh; + * xdrproc_t xres; + * caddr_t resp; + */ +#define CLNT_FREERES(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp)) +#define clnt_freeres(rh,xres,resp) ((*(rh)->cl_ops->cl_freeres)(rh,xres,resp)) + +/* + * bool_t + * CLNT_CONTROL(cl, request, info) + * CLIENT *cl; + * unsigned int request; + * char *info; + */ +#define CLNT_CONTROL(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in)) +#define clnt_control(cl,rq,in) ((*(cl)->cl_ops->cl_control)(cl,rq,in)) + +/* + * control operations that apply to both udp and tcp transports + */ +#define CLSET_TIMEOUT 1 /* set timeout (timeval) */ +#define CLGET_TIMEOUT 2 /* get timeout (timeval) */ +#define CLGET_SERVER_ADDR 3 /* get server's address (sockaddr) */ +/* + * udp only control operations + */ +#define CLSET_RETRY_TIMEOUT 4 /* set retry timeout (timeval) */ +#define CLGET_RETRY_TIMEOUT 5 /* get retry timeout (timeval) */ +/* + * new control operations + */ +#define CLGET_LOCAL_ADDR 6 /* get local address (sockaddr, getsockname)*/ + +/* + * void + * CLNT_DESTROY(rh); + * CLIENT *rh; + */ +#define CLNT_DESTROY(rh) ((*(rh)->cl_ops->cl_destroy)(rh)) +#define clnt_destroy(rh) ((*(rh)->cl_ops->cl_destroy)(rh)) + + +/* + * RPCTEST is a test program which is accessable on every rpc + * transport/port. It is used for testing, performance evaluation, + * and network administration. + */ + +#define RPCTEST_PROGRAM ((rpc_u_int32)1) +#define RPCTEST_VERSION ((rpc_u_int32)1) +#define RPCTEST_NULL_PROC ((rpc_u_int32)2) +#define RPCTEST_NULL_BATCH_PROC ((rpc_u_int32)3) + +/* + * By convention, procedure 0 takes null arguments and returns them + */ + +#define NULLPROC ((rpc_u_int32)0) + +/* + * Below are the client handle creation routines for the various + * implementations of client side rpc. They can return NULL if a + * creation failure occurs. + */ + +/* + * Memory based rpc (for speed check and testing) + * CLIENT * + * clntraw_create(prog, vers) + * rpc_u_int32 prog; + * rpc_u_int32 vers; + */ +extern CLIENT *clntraw_create(); + + +/* + * Generic client creation routine. Supported protocols are "udp" and "tcp" + */ +extern CLIENT * +clnt_create(/*host, prog, vers, prot*/); /* + char *host; -- hostname + rpc_u_int32 prog; -- program number + rpc_u_int32 vers; -- version number + char *prot; -- protocol +*/ + + + + +/* + * TCP based rpc + * CLIENT * + * clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz) + * struct sockaddr_in *raddr; + * rpc_u_int32 prog; + * rpc_u_int32 version; + * register int *sockp; + * unsigned int sendsz; + * unsigned int recvsz; + */ +extern CLIENT *clnttcp_create(); + +/* + * UDP based rpc. + * CLIENT * + * clntudp_create(raddr, program, version, wait, sockp) + * struct sockaddr_in *raddr; + * rpc_u_int32 program; + * rpc_u_int32 version; + * struct timeval wait; + * int *sockp; + * + * Same as above, but you specify max packet sizes. + * CLIENT * + * clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) + * struct sockaddr_in *raddr; + * rpc_u_int32 program; + * rpc_u_int32 version; + * struct timeval wait; + * int *sockp; + * unsigned int sendsz; + * unsigned int recvsz; + */ +extern CLIENT *clntudp_create(); +extern CLIENT *clntudp_bufcreate(); + +/* + * Print why creation failed + */ +void clnt_pcreateerror(/* char *msg */); /* stderr */ +char *clnt_spcreateerror(/* char *msg */); /* string */ + +/* + * Like clnt_perror(), but is more verbose in its output + */ +void clnt_perrno(/* enum clnt_stat num */); /* stderr */ + +/* + * Print an English error message, given the client error code + */ +void clnt_perror(/* CLIENT *clnt, char *msg */); /* stderr */ +char *clnt_sperror(/* CLIENT *clnt, char *msg */); /* string */ + +/* + * If a creation fails, the following allows the user to figure out why. + */ +struct rpc_createerr { + enum clnt_stat cf_stat; + struct rpc_err cf_error; /* useful when cf_stat == RPC_PMAPFAILURE */ +}; + +extern struct rpc_createerr rpc_createerr; + + + +/* + * Copy error message to buffer. + */ +char *clnt_sperrno(/* enum clnt_stat num */); /* string */ + + + +#define UDPMSGSIZE 8800 /* rpc imposed limit on udp msg size */ +#define RPCSMALLMSGSIZE 400 /* a more reasonable packet size */ + +#endif /*!_CLNT_*/ diff --git a/src/lib/rpc/clnt_generic.c b/src/lib/rpc/clnt_generic.c new file mode 100644 index 000000000..f111c2e14 --- /dev/null +++ b/src/lib/rpc/clnt_generic.c @@ -0,0 +1,110 @@ +/* @(#)clnt_generic.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_generic.c 1.4 87/08/11 (C) 1987 SMI"; +#endif +/* + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include +#include +#include +#include + +/* + * Generic client creation: takes (hostname, program-number, protocol) and + * returns client handle. Default options are set, which the user can + * change using the rpc equivalent of ioctl()'s. + */ +CLIENT * +clnt_create(hostname, prog, vers, proto) + char *hostname; + unsigned prog; + unsigned vers; + char *proto; +{ + struct hostent *h; + struct protoent *p; + struct sockaddr_in sin; + int sock; + struct timeval tv; + CLIENT *client; + + h = gethostbyname(hostname); + if (h == NULL) { + rpc_createerr.cf_stat = RPC_UNKNOWNHOST; + return (NULL); + } + if (h->h_addrtype != AF_INET) { + /* + * Only support INET for now + */ + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = EAFNOSUPPORT; + return (NULL); + } + sin.sin_family = h->h_addrtype; + sin.sin_port = 0; + memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); + memmove((char*)&sin.sin_addr, h->h_addr, h->h_length); + p = getprotobyname(proto); + if (p == NULL) { + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + rpc_createerr.cf_error.re_errno = EPFNOSUPPORT; + return (NULL); + } + sock = RPC_ANYSOCK; + switch (p->p_proto) { + case IPPROTO_UDP: + tv.tv_sec = 5; + tv.tv_usec = 0; + client = clntudp_create(&sin, prog, vers, tv, &sock); + if (client == NULL) { + return (NULL); + } + tv.tv_sec = 25; + clnt_control(client, CLSET_TIMEOUT, &tv); + break; + case IPPROTO_TCP: + client = clnttcp_create(&sin, prog, vers, &sock, 0, 0); + if (client == NULL) { + return (NULL); + } + tv.tv_sec = 25; + tv.tv_usec = 0; + clnt_control(client, CLSET_TIMEOUT, &tv); + break; + default: + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = EPFNOSUPPORT; + return (NULL); + } + return (client); +} diff --git a/src/lib/rpc/clnt_perror.c b/src/lib/rpc/clnt_perror.c new file mode 100644 index 000000000..63eef49f0 --- /dev/null +++ b/src/lib/rpc/clnt_perror.c @@ -0,0 +1,306 @@ +/* @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_perror.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + */ +#include +#include + +#include +#include +#include + +#ifdef NEED_SYS_ERRLIST +extern char *sys_errlist[]; +#endif +static char *auth_errmsg(); + +extern char *strcpy(); + +static char *buf; + +static char * +_buf() +{ + + if (buf == 0) + buf = (char *)malloc(256); + return (buf); +} + +/* + * Print reply error info + */ +char * +clnt_sperror(rpch, s) + CLIENT *rpch; + char *s; +{ + struct rpc_err e; + void clnt_perrno(); + char *err; + char *str = _buf(); + char *strstart = str; + + if (str == 0) + return (0); + CLNT_GETERR(rpch, &e); + + (void) sprintf(str, "%s: ", s); + str += strlen(str); + + (void) strcpy(str, clnt_sperrno(e.re_status)); + str += strlen(str); + + switch (e.re_status) { + case RPC_SUCCESS: + case RPC_CANTENCODEARGS: + case RPC_CANTDECODERES: + case RPC_TIMEDOUT: + case RPC_PROGUNAVAIL: + case RPC_PROCUNAVAIL: + case RPC_CANTDECODEARGS: + case RPC_SYSTEMERROR: + case RPC_UNKNOWNHOST: + case RPC_UNKNOWNPROTO: + case RPC_PMAPFAILURE: + case RPC_PROGNOTREGISTERED: + case RPC_FAILED: + break; + + case RPC_CANTSEND: + case RPC_CANTRECV: + (void) sprintf(str, "; errno = %s", + sys_errlist[e.re_errno]); + str += strlen(str); + break; + + case RPC_VERSMISMATCH: + (void) sprintf(str, + "; low version = %lu, high version = %lu", + e.re_vers.low, e.re_vers.high); + str += strlen(str); + break; + + case RPC_AUTHERROR: + err = auth_errmsg(e.re_why); + (void) sprintf(str,"; why = "); + str += strlen(str); + if (err != NULL) { + (void) sprintf(str, "%s",err); + } else { + (void) sprintf(str, + "(unknown authentication error - %d)", + (int) e.re_why); + } + str += strlen(str); + break; + + case RPC_PROGVERSMISMATCH: + (void) sprintf(str, + "; low version = %lu, high version = %lu", + e.re_vers.low, e.re_vers.high); + str += strlen(str); + break; + + default: /* unknown */ + (void) sprintf(str, + "; s1 = %lu, s2 = %lu", + e.re_lb.s1, e.re_lb.s2); + str += strlen(str); + break; + } + (void) sprintf(str, "\n"); + return(strstart) ; +} + +void +clnt_perror(rpch, s) + CLIENT *rpch; + char *s; +{ + (void) fprintf(stderr,"%s",clnt_sperror(rpch,s)); +} + + +struct rpc_errtab { + enum clnt_stat status; + char *message; +}; + +static struct rpc_errtab rpc_errlist[] = { + { RPC_SUCCESS, + "RPC: Success" }, + { RPC_CANTENCODEARGS, + "RPC: Can't encode arguments" }, + { RPC_CANTDECODERES, + "RPC: Can't decode result" }, + { RPC_CANTSEND, + "RPC: Unable to send" }, + { RPC_CANTRECV, + "RPC: Unable to receive" }, + { RPC_TIMEDOUT, + "RPC: Timed out" }, + { RPC_VERSMISMATCH, + "RPC: Incompatible versions of RPC" }, + { RPC_AUTHERROR, + "RPC: Authentication error" }, + { RPC_PROGUNAVAIL, + "RPC: Program unavailable" }, + { RPC_PROGVERSMISMATCH, + "RPC: Program/version mismatch" }, + { RPC_PROCUNAVAIL, + "RPC: Procedure unavailable" }, + { RPC_CANTDECODEARGS, + "RPC: Server can't decode arguments" }, + { RPC_SYSTEMERROR, + "RPC: Remote system error" }, + { RPC_UNKNOWNHOST, + "RPC: Unknown host" }, + { RPC_UNKNOWNPROTO, + "RPC: Unknown protocol" }, + { RPC_PMAPFAILURE, + "RPC: Port mapper failure" }, + { RPC_PROGNOTREGISTERED, + "RPC: Program not registered"}, + { RPC_FAILED, + "RPC: Failed (unspecified error)"} +}; + + +/* + * This interface for use by clntrpc + */ +char * +clnt_sperrno(stat) + enum clnt_stat stat; +{ + int i; + + for (i = 0; i < sizeof(rpc_errlist)/sizeof(struct rpc_errtab); i++) { + if (rpc_errlist[i].status == stat) { + return (rpc_errlist[i].message); + } + } + return ("RPC: (unknown error code)"); +} + +void +clnt_perrno(num) + enum clnt_stat num; +{ + (void) fprintf(stderr,"%s",clnt_sperrno(num)); +} + + +char * +clnt_spcreateerror(s) + char *s; +{ + extern int sys_nerr; + char *str = _buf(); + + if (str == 0) + return(0); + (void) sprintf(str, "%s: ", s); + (void) strcat(str, clnt_sperrno(rpc_createerr.cf_stat)); + switch (rpc_createerr.cf_stat) { + case RPC_PMAPFAILURE: + (void) strcat(str, " - "); + (void) strcat(str, + clnt_sperrno(rpc_createerr.cf_error.re_status)); + break; + + case RPC_SYSTEMERROR: + (void) strcat(str, " - "); + if (rpc_createerr.cf_error.re_errno > 0 + && rpc_createerr.cf_error.re_errno < sys_nerr) + (void) strcat(str, + sys_errlist[rpc_createerr.cf_error.re_errno]); + else + (void) sprintf(&str[strlen(str)], "Error %d", + rpc_createerr.cf_error.re_errno); + break; + } + (void) strcat(str, "\n"); + return (str); +} + +void +clnt_pcreateerror(s) + char *s; +{ + (void) fprintf(stderr,"%s",clnt_spcreateerror(s)); +} + +struct auth_errtab { + enum auth_stat status; + char *message; +}; + +static struct auth_errtab auth_errlist[] = { + { AUTH_OK, + "Authentication OK" }, + { AUTH_BADCRED, + "Invalid client credential" }, + { AUTH_REJECTEDCRED, + "Server rejected credential" }, + { AUTH_BADVERF, + "Invalid client verifier" }, + { AUTH_REJECTEDVERF, + "Server rejected verifier" }, + { AUTH_TOOWEAK, + "Client credential too weak" }, + { AUTH_INVALIDRESP, + "Invalid server verifier" }, + { AUTH_FAILED, + "Failed (unspecified error)" }, +}; + +static char * +auth_errmsg(stat) + enum auth_stat stat; +{ + int i; + + for (i = 0; i < sizeof(auth_errlist)/sizeof(struct auth_errtab); i++) { + if (auth_errlist[i].status == stat) { + return(auth_errlist[i].message); + } + } + return(NULL); +} diff --git a/src/lib/rpc/clnt_raw.c b/src/lib/rpc/clnt_raw.c new file mode 100644 index 000000000..d05dbaa7e --- /dev/null +++ b/src/lib/rpc/clnt_raw.c @@ -0,0 +1,239 @@ +/* @(#)clnt_raw.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_raw.c 1.22 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_raw.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Memory based rpc for simple testing and timing. + * Interface to create an rpc client and server in the same process. + * This lets us similate rpc and get round trip overhead, without + * any interference from the kernal. + */ + +#include + +#define MCALL_MSG_SIZE 24 + +/* + * This is the "network" we will be moving stuff over. + */ +static struct clntraw_private { + CLIENT client_object; + XDR xdr_stream; + char _raw_buf[UDPMSGSIZE]; + char mashl_callmsg[MCALL_MSG_SIZE]; + unsigned int mcnt; +} *clntraw_private; + +static enum clnt_stat clntraw_call(); +static void clntraw_abort(); +static void clntraw_geterr(); +static bool_t clntraw_freeres(); +static bool_t clntraw_control(); +static void clntraw_destroy(); + +static struct clnt_ops client_ops = { + clntraw_call, + clntraw_abort, + clntraw_geterr, + clntraw_freeres, + clntraw_destroy, + clntraw_control +}; + +void svc_getreq(); + +/* + * Create a client handle for memory based rpc. + */ +CLIENT * +clntraw_create(prog, vers) + rpc_u_int32 prog; + rpc_u_int32 vers; +{ + register struct clntraw_private *clp = clntraw_private; + struct rpc_msg call_msg; + XDR *xdrs = &clp->xdr_stream; + CLIENT *client = &clp->client_object; + + if (clp == 0) { + clp = (struct clntraw_private *)calloc(1, sizeof (*clp)); + if (clp == 0) + return (0); + clntraw_private = clp; + } + /* + * pre-serialize the staic part of the call msg and stash it away + */ + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + xdrmem_create(xdrs, clp->mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE); + if (! xdr_callhdr(xdrs, &call_msg)) { + perror("clnt_raw.c - Fatal header serialization error."); + } + clp->mcnt = XDR_GETPOS(xdrs); + XDR_DESTROY(xdrs); + + /* + * Set xdrmem for client/server shared buffer + */ + xdrmem_create(xdrs, clp->_raw_buf, UDPMSGSIZE, XDR_FREE); + + /* + * create client handle + */ + client->cl_ops = &client_ops; + client->cl_auth = authnone_create(); + return (client); +} + +static enum clnt_stat +clntraw_call(h, proc, xargs, argsp, xresults, resultsp, timeout) + CLIENT *h; + rpc_u_int32 proc; + xdrproc_t xargs; + caddr_t argsp; + xdrproc_t xresults; + caddr_t resultsp; + struct timeval timeout; +{ + register struct clntraw_private *clp = clntraw_private; + register XDR *xdrs = &clp->xdr_stream; + struct rpc_msg msg; + enum clnt_stat status; + struct rpc_err error; + long procl = proc; + + if (clp == 0) + return (RPC_FAILED); +call_again: + /* + * send request + */ + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, 0); + ((struct rpc_msg *)clp->mashl_callmsg)->rm_xid ++ ; + if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) || + (! XDR_PUTLONG(xdrs, &procl)) || + (! AUTH_MARSHALL(h->cl_auth, xdrs)) || + (! (*xargs)(xdrs, argsp))) { + return (RPC_CANTENCODEARGS); + } + (void)XDR_GETPOS(xdrs); /* called just to cause overhead */ + + /* + * We have to call server input routine here because this is + * all going on in one process. Yuk. + */ + svc_getreq(1); + + /* + * get results + */ + xdrs->x_op = XDR_DECODE; + XDR_SETPOS(xdrs, 0); + msg.acpted_rply.ar_verf = _null_auth; + msg.acpted_rply.ar_results.where = resultsp; + msg.acpted_rply.ar_results.proc = xresults; + if (! xdr_replymsg(xdrs, &msg)) + return (RPC_CANTDECODERES); + sunrpc_seterr_reply(&msg, &error); + status = error.re_status; + + if (status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) { + status = RPC_AUTHERROR; + } + } /* end successful completion */ + else { + if (AUTH_REFRESH(h->cl_auth, &msg)) + goto call_again; + } /* end of unsuccessful completion */ + + if (status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) { + status = RPC_AUTHERROR; + } + if (msg.acpted_rply.ar_verf.oa_base != NULL) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, &(msg.acpted_rply.ar_verf)); + } + } + + return (status); +} + +static void +clntraw_geterr() +{ +} + + +static bool_t +clntraw_freeres(cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + register struct clntraw_private *clp = clntraw_private; + register XDR *xdrs = &clp->xdr_stream; + bool_t rval; + + if (clp == 0) + { + rval = (bool_t) RPC_FAILED; + return (rval); + } + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +static void +clntraw_abort() +{ +} + +static bool_t +clntraw_control() +{ + return (FALSE); +} + +static void +clntraw_destroy() +{ +} diff --git a/src/lib/rpc/clnt_simple.c b/src/lib/rpc/clnt_simple.c new file mode 100644 index 000000000..0d8f7a4df --- /dev/null +++ b/src/lib/rpc/clnt_simple.c @@ -0,0 +1,112 @@ +/* @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_simple.c + * Simplified front end to rpc. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include + +static struct callrpc_private { + CLIENT *client; + int socket; + int oldprognum, oldversnum, valid; + char *oldhost; +} *callrpc_private; + +callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out) + char *host; + xdrproc_t inproc, outproc; + char *in, *out; +{ + register struct callrpc_private *crp = callrpc_private; + struct sockaddr_in server_addr; + enum clnt_stat clnt_stat; + struct hostent *hp; + struct timeval timeout, tottimeout; + + if (crp == 0) { + crp = (struct callrpc_private *)calloc(1, sizeof (*crp)); + if (crp == 0) + return (0); + callrpc_private = crp; + } + if (crp->oldhost == NULL) { + crp->oldhost = mem_alloc(256); + crp->oldhost[0] = 0; + crp->socket = RPC_ANYSOCK; + } + if (crp->valid && crp->oldprognum == prognum && crp->oldversnum == versnum + && strcmp(crp->oldhost, host) == 0) { + /* reuse old client */ + } else { + crp->valid = 0; + (void)close(crp->socket); + crp->socket = RPC_ANYSOCK; + if (crp->client) { + clnt_destroy(crp->client); + crp->client = NULL; + } + if ((hp = gethostbyname(host)) == NULL) + return ((int) RPC_UNKNOWNHOST); + timeout.tv_usec = 0; + timeout.tv_sec = 5; + memmove((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length); + server_addr.sin_family = AF_INET; + server_addr.sin_port = 0; + if ((crp->client = clntudp_create(&server_addr, (rpc_u_int32)prognum, + (rpc_u_int32)versnum, timeout, &crp->socket)) == NULL) + return ((int) rpc_createerr.cf_stat); + crp->valid = 1; + crp->oldprognum = prognum; + crp->oldversnum = versnum; + (void) strcpy(crp->oldhost, host); + } + tottimeout.tv_sec = 25; + tottimeout.tv_usec = 0; + clnt_stat = clnt_call(crp->client, procnum, inproc, in, + outproc, out, tottimeout); + /* + * if call failed, empty cache + */ + if (clnt_stat != RPC_SUCCESS) + crp->valid = 0; + return ((int) clnt_stat); +} diff --git a/src/lib/rpc/clnt_tcp.c b/src/lib/rpc/clnt_tcp.c new file mode 100644 index 000000000..c573897f7 --- /dev/null +++ b/src/lib/rpc/clnt_tcp.c @@ -0,0 +1,476 @@ +/* @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +#include +#include +#include +#include +#include +#include + +#define MCALL_MSG_SIZE 24 + +extern int errno; + +static int readtcp(); +static int writetcp(); + +static enum clnt_stat clnttcp_call(); +static void clnttcp_abort(); +static void clnttcp_geterr(); +static bool_t clnttcp_freeres(); +static bool_t clnttcp_control(); +static void clnttcp_destroy(); + +static struct clnt_ops tcp_ops = { + clnttcp_call, + clnttcp_abort, + clnttcp_geterr, + clnttcp_freeres, + clnttcp_destroy, + clnttcp_control +}; + +struct ct_data { + int ct_sock; + bool_t ct_closeit; + struct timeval ct_wait; + bool_t ct_waitset; /* wait set by clnt_control? */ + struct sockaddr_in ct_addr; + struct rpc_err ct_error; + char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */ + unsigned int ct_mpos; /* pos after marshal */ + XDR ct_xdrs; +}; + +/* + * Create a client handle for a tcp/ip connection. + * If *sockp<0, *sockp is set to a newly created TCP socket and it is + * connected to raddr. If *sockp non-negative then + * raddr is ignored. The rpc/tcp package does buffering + * similar to stdio, so the client must pick send and receive buffer sizes,]; + * 0 => use the default. + * If raddr->sin_port is 0, then a binder on the remote machine is + * consulted for the right port number. + * NB: *sockp is copied into a private area. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this + * something more useful. + */ +CLIENT * +clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz) + struct sockaddr_in *raddr; + rpc_u_int32 prog; + rpc_u_int32 vers; + register int *sockp; + unsigned int sendsz; + unsigned int recvsz; +{ + CLIENT *h; + register struct ct_data *ct; + struct timeval now; + struct rpc_msg call_msg; + + h = (CLIENT *)mem_alloc(sizeof(*h)); + if (h == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + ct = (struct ct_data *)mem_alloc(sizeof(*ct)); + if (ct == NULL) { + (void)fprintf(stderr, "clnttcp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + + /* + * If no port number given ask the pmap for one + */ + if (raddr->sin_port == 0) { + unsigned short port; + if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { + mem_free((caddr_t)ct, sizeof(struct ct_data)); + mem_free((caddr_t)h, sizeof(CLIENT)); + return ((CLIENT *)NULL); + } + raddr->sin_port = htons(port); + } + + /* + * If no socket given, open one + */ + if (*sockp < 0) { + *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + if ((*sockp < 0) + || (connect(*sockp, (struct sockaddr *)raddr, + sizeof(*raddr)) < 0)) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + (void)close(*sockp); + goto fooy; + } + ct->ct_closeit = TRUE; + } else { + ct->ct_closeit = FALSE; + } + + /* + * Set up private data struct + */ + ct->ct_sock = *sockp; + ct->ct_wait.tv_usec = 0; + ct->ct_waitset = FALSE; + ct->ct_addr = *raddr; + + /* + * Initialize call message + */ + (void)gettimeofday(&now, (struct timezone *)0); + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = prog; + call_msg.rm_call.cb_vers = vers; + + /* + * pre-serialize the staic part of the call msg and stash it away + */ + xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE, + XDR_ENCODE); + if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) { + if (ct->ct_closeit) { + (void)close(*sockp); + } + goto fooy; + } + ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs)); + XDR_DESTROY(&(ct->ct_xdrs)); + + /* + * Create a client handle which uses xdrrec for serialization + * and authnone for authentication. + */ + xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz, + (caddr_t)ct, readtcp, writetcp); + h->cl_ops = &tcp_ops; + h->cl_private = (caddr_t) ct; + h->cl_auth = authnone_create(); + return (h); + +fooy: + /* + * Something goofed, free stuff and barf + */ + mem_free((caddr_t)ct, sizeof(struct ct_data)); + mem_free((caddr_t)h, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +static enum clnt_stat +clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout) + register CLIENT *h; + rpc_u_int32 proc; + xdrproc_t xdr_args; + caddr_t args_ptr; + xdrproc_t xdr_results; + caddr_t results_ptr; + struct timeval timeout; +{ + register struct ct_data *ct = (struct ct_data *) h->cl_private; + register XDR *xdrs = &(ct->ct_xdrs); + struct rpc_msg reply_msg; + rpc_u_int32 x_id; + rpc_u_int32 *msg_x_id = (rpc_u_int32 *)(ct->ct_mcall); /* yuk */ + register bool_t shipnow; + int refreshes = 2; + long procl = proc; + + if (!ct->ct_waitset) { + ct->ct_wait = timeout; + } + + shipnow = + (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0 + && timeout.tv_usec == 0) ? FALSE : TRUE; + +call_again: + xdrs->x_op = XDR_ENCODE; + ct->ct_error.re_status = RPC_SUCCESS; + x_id = ntohl(--(*msg_x_id)); + if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) || + (! XDR_PUTLONG(xdrs, &procl)) || + (! AUTH_MARSHALL(h->cl_auth, xdrs)) || + (! AUTH_WRAP(h->cl_auth, xdrs, xdr_args, args_ptr))) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTENCODEARGS; + (void)xdrrec_endofrecord(xdrs, TRUE); + return (ct->ct_error.re_status); + } + if (! xdrrec_endofrecord(xdrs, shipnow)) + return (ct->ct_error.re_status = RPC_CANTSEND); + if (! shipnow) + return (RPC_SUCCESS); + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + return(ct->ct_error.re_status = RPC_TIMEDOUT); + } + + + /* + * Keep receiving until we get a valid transaction id + */ + xdrs->x_op = XDR_DECODE; + while (TRUE) { + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = xdr_void; + if (! xdrrec_skiprecord(xdrs)) + return (ct->ct_error.re_status); + /* now decode and validate the response header */ + if (! xdr_replymsg(xdrs, &reply_msg)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + continue; + return (ct->ct_error.re_status); + } + if (reply_msg.rm_xid == x_id) + break; + } + + /* + * process header + */ + sunrpc_seterr_reply(&reply_msg, &(ct->ct_error)); + if (ct->ct_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) { + ct->ct_error.re_status = RPC_AUTHERROR; + ct->ct_error.re_why = AUTH_INVALIDRESP; + } else if (! AUTH_UNWRAP(h->cl_auth, xdrs, + xdr_results, results_ptr)) { + if (ct->ct_error.re_status == RPC_SUCCESS) + ct->ct_error.re_status = RPC_CANTDECODERES; + } + } /* end successful completion */ + else { + /* maybe our credentials need to be refreshed ... */ + if (refreshes-- && AUTH_REFRESH(h->cl_auth, &reply_msg)) + goto call_again; + } /* end of unsuccessful completion */ + /* free verifier ... */ + if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && + (reply_msg.acpted_rply.ar_verf.oa_base != NULL)) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf)); + } + return (ct->ct_error.re_status); +} + +static void +clnttcp_geterr(h, errp) + CLIENT *h; + struct rpc_err *errp; +{ + register struct ct_data *ct = + (struct ct_data *) h->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnttcp_freeres(cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + register struct ct_data *ct = (struct ct_data *)cl->cl_private; + register XDR *xdrs = &(ct->ct_xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +static void +clnttcp_abort() +{ +} + +static bool_t +clnttcp_control(cl, request, info) + CLIENT *cl; + int request; + char *info; +{ + register struct ct_data *ct = (struct ct_data *)cl->cl_private; + int len; + + switch (request) { + case CLSET_TIMEOUT: + ct->ct_wait = *(struct timeval *)info; + ct->ct_waitset = TRUE; + break; + case CLGET_TIMEOUT: + *(struct timeval *)info = ct->ct_wait; + break; + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = ct->ct_addr; + break; + case CLGET_LOCAL_ADDR: + len = sizeof(struct sockaddr); + if (getsockname(ct->ct_sock, (struct sockaddr*)info, &len) < 0) + return FALSE; + else + return TRUE; + default: + return (FALSE); + } + return (TRUE); +} + + +static void +clnttcp_destroy(h) + CLIENT *h; +{ + register struct ct_data *ct = + (struct ct_data *) h->cl_private; + + if (ct->ct_closeit) { + (void)close(ct->ct_sock); + } + XDR_DESTROY(&(ct->ct_xdrs)); + mem_free((caddr_t)ct, sizeof(struct ct_data)); + mem_free((caddr_t)h, sizeof(CLIENT)); +} + +/* + * Interface between xdr serializer and tcp connection. + * Behaves like the system calls, read & write, but keeps some error state + * around for the rpc level. + */ +static int +readtcp(ct, buf, len) + register struct ct_data *ct; + caddr_t buf; + register int len; +{ +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + if (len == 0) + return (0); + FD_ZERO(&mask); + FD_SET(ct->ct_sock, &mask); +#else + register int mask = 1 << (ct->ct_sock); + int readfds; + + if (len == 0) + return (0); + +#endif /* def FD_SETSIZE */ + while (TRUE) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, (fd_set*)NULL, (fd_set*)NULL, + &(ct->ct_wait))) { + case 0: + ct->ct_error.re_status = RPC_TIMEDOUT; + return (-1); + + case -1: + if (errno == EINTR) + continue; + ct->ct_error.re_status = RPC_CANTRECV; + ct->ct_error.re_errno = errno; + return (-1); + } + break; + } + switch (len = read(ct->ct_sock, buf, len)) { + + case 0: + /* premature eof */ + ct->ct_error.re_errno = ECONNRESET; + ct->ct_error.re_status = RPC_CANTRECV; + len = -1; /* it's really an error */ + break; + + case -1: + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTRECV; + break; + } + return (len); +} + +static int +writetcp(ct, buf, len) + struct ct_data *ct; + caddr_t buf; + int len; +{ + register int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) { + if ((i = write(ct->ct_sock, buf, cnt)) == -1) { + ct->ct_error.re_errno = errno; + ct->ct_error.re_status = RPC_CANTSEND; + return (-1); + } + } + return (len); +} diff --git a/src/lib/rpc/clnt_udp.c b/src/lib/rpc/clnt_udp.c new file mode 100644 index 000000000..000b9683d --- /dev/null +++ b/src/lib/rpc/clnt_udp.c @@ -0,0 +1,460 @@ +/* @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * clnt_udp.c, Implements a UDP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#if defined(sparc) +#include +#endif +#include +#include +#include + +extern int errno; + +/* + * UDP bases client side rpc operations + */ +static enum clnt_stat clntudp_call(); +static void clntudp_abort(); +static void clntudp_geterr(); +static bool_t clntudp_freeres(); +static bool_t clntudp_control(); +static void clntudp_destroy(); + +static struct clnt_ops udp_ops = { + clntudp_call, + clntudp_abort, + clntudp_geterr, + clntudp_freeres, + clntudp_destroy, + clntudp_control +}; + +/* + * Private data kept per client handle + */ +struct cu_data { + int cu_sock; + bool_t cu_closeit; + struct sockaddr_in cu_raddr; + int cu_rlen; + struct timeval cu_wait; + struct timeval cu_total; + struct rpc_err cu_error; + XDR cu_outxdrs; + unsigned int cu_xdrpos; + unsigned int cu_sendsz; + char *cu_outbuf; + unsigned int cu_recvsz; + char cu_inbuf[1]; +}; + +/* + * Create a UDP based client handle. + * If *sockp<0, *sockp is set to a newly created UPD socket. + * If raddr->sin_port is 0 a binder on the remote machine + * is consulted for the correct port number. + * NB: It is the clients responsibility to close *sockp. + * NB: The rpch->cl_auth is initialized to null authentication. + * Caller may wish to set this something more useful. + * + * wait is the amount of time used between retransmitting a call if + * no response has been heard; retransmition occurs until the actual + * rpc call times out. + * + * sendsz and recvsz are the maximum allowable packet sizes that can be + * sent and received. + */ +CLIENT * +clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz) + struct sockaddr_in *raddr; + rpc_u_int32 program; + rpc_u_int32 version; + struct timeval wait; + register int *sockp; + unsigned int sendsz; + unsigned int recvsz; +{ + CLIENT *cl; + register struct cu_data *cu; + struct timeval now; + struct rpc_msg call_msg; + + cl = (CLIENT *)mem_alloc(sizeof(CLIENT)); + if (cl == NULL) { + (void) fprintf(stderr, "clntudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + sendsz = ((sendsz + 3) / 4) * 4; + recvsz = ((recvsz + 3) / 4) * 4; + cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz); + if (cu == NULL) { + (void) fprintf(stderr, "clntudp_create: out of memory\n"); + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + cu->cu_outbuf = &cu->cu_inbuf[recvsz]; + + (void)gettimeofday(&now, (struct timezone *)0); + if (raddr->sin_port == 0) { + unsigned short port; + if ((port = + pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) { + goto fooy; + } + raddr->sin_port = htons(port); + } + cl->cl_ops = &udp_ops; + cl->cl_private = (caddr_t)cu; + cu->cu_raddr = *raddr; + cu->cu_rlen = sizeof (cu->cu_raddr); + cu->cu_wait = wait; + cu->cu_total.tv_sec = -1; + cu->cu_total.tv_usec = -1; + cu->cu_sendsz = sendsz; + cu->cu_recvsz = recvsz; + call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = program; + call_msg.rm_call.cb_vers = version; + xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, + sendsz, XDR_ENCODE); + if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) { + goto fooy; + } + cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs)); + if (*sockp < 0) { + int dontblock = 1; + + *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (*sockp < 0) { + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + rpc_createerr.cf_error.re_errno = errno; + goto fooy; + } + /* attempt to bind to prov port */ + (void)bindresvport(*sockp, (struct sockaddr_in *)0); + /* the sockets rpc controls are non-blocking */ + (void)ioctl(*sockp, FIONBIO, (char *) &dontblock); + cu->cu_closeit = TRUE; + } else { + cu->cu_closeit = FALSE; + } + cu->cu_sock = *sockp; + cl->cl_auth = authnone_create(); + return (cl); +fooy: + if (cu) + mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz); + if (cl) + mem_free((caddr_t)cl, sizeof(CLIENT)); + return ((CLIENT *)NULL); +} + +CLIENT * +clntudp_create(raddr, program, version, wait, sockp) + struct sockaddr_in *raddr; + rpc_u_int32 program; + rpc_u_int32 version; + struct timeval wait; + register int *sockp; +{ + + return(clntudp_bufcreate(raddr, program, version, wait, sockp, + UDPMSGSIZE, UDPMSGSIZE)); +} + +static enum clnt_stat +clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout) + register CLIENT *cl; /* client handle */ + rpc_u_int32 proc; /* procedure number */ + xdrproc_t xargs; /* xdr routine for args */ + caddr_t argsp; /* pointer to args */ + xdrproc_t xresults; /* xdr routine for results */ + caddr_t resultsp; /* pointer to results */ + struct timeval utimeout; /* seconds to wait before giving up */ +{ + register struct cu_data *cu = (struct cu_data *)cl->cl_private; + register XDR *xdrs; + register int outlen; + register int inlen; + int fromlen; +#ifdef FD_SETSIZE + fd_set readfds; + fd_set mask; +#else + int readfds; + register int mask; +#endif /* def FD_SETSIZE */ + struct sockaddr_in from; + struct rpc_msg reply_msg; + XDR reply_xdrs; + struct timeval time_waited; + bool_t ok; + int nrefreshes = 2; /* number of times to refresh cred */ + struct timeval timeout; + long procl = proc; + + if (cu->cu_total.tv_usec == -1) { + timeout = utimeout; /* use supplied timeout */ + } else { + timeout = cu->cu_total; /* use default timeout */ + } + + time_waited.tv_sec = 0; + time_waited.tv_usec = 0; +call_again: + xdrs = &(cu->cu_outxdrs); + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, cu->cu_xdrpos); + /* + * the transaction is the first thing in the out buffer + */ + (*(unsigned short *)(cu->cu_outbuf))++; + if ((! XDR_PUTLONG(xdrs, &procl)) || + (! AUTH_MARSHALL(cl->cl_auth, xdrs)) || + (! AUTH_WRAP(cl->cl_auth, xdrs, xargs, argsp))) + return (cu->cu_error.re_status = RPC_CANTENCODEARGS); + outlen = (int)XDR_GETPOS(xdrs); + +send_again: + if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0, + (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) + != outlen) { + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTSEND); + } + + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + return (cu->cu_error.re_status = RPC_TIMEDOUT); + } + /* + * sub-optimal code appears here because we have + * some clock time to spare while the packets are in flight. + * (We assume that this is actually only executed once.) + */ + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = xdr_void; +#ifdef FD_SETSIZE + FD_ZERO(&mask); + FD_SET(cu->cu_sock, &mask); +#else + mask = 1 << cu->cu_sock; +#endif /* def FD_SETSIZE */ + for (;;) { + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, (fd_set *)NULL, + (fd_set *)NULL, &(cu->cu_wait))) { + + case 0: + time_waited.tv_sec += cu->cu_wait.tv_sec; + time_waited.tv_usec += cu->cu_wait.tv_usec; + while (time_waited.tv_usec >= 1000000) { + time_waited.tv_sec++; + time_waited.tv_usec -= 1000000; + } + if ((time_waited.tv_sec < timeout.tv_sec) || + ((time_waited.tv_sec == timeout.tv_sec) && + (time_waited.tv_usec < timeout.tv_usec))) + goto send_again; + return (cu->cu_error.re_status = RPC_TIMEDOUT); + + /* + * buggy in other cases because time_waited is not being + * updated. + */ + case -1: + if (errno == EINTR) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + do { + fromlen = sizeof(struct sockaddr); + inlen = recvfrom(cu->cu_sock, cu->cu_inbuf, + (int) cu->cu_recvsz, 0, + (struct sockaddr *)&from, &fromlen); + } while (inlen < 0 && errno == EINTR); + if (inlen < 0) { + if (errno == EWOULDBLOCK) + continue; + cu->cu_error.re_errno = errno; + return (cu->cu_error.re_status = RPC_CANTRECV); + } + if (inlen < sizeof(rpc_u_int32)) + continue; + /* see if reply transaction id matches sent id */ + if (*((rpc_u_int32 *)(cu->cu_inbuf)) != *((rpc_u_int32 *)(cu->cu_outbuf))) + continue; + /* we now assume we have the proper reply */ + break; + } + + /* + * now decode and validate the response + */ + xdrmem_create(&reply_xdrs, cu->cu_inbuf, (unsigned int)inlen, XDR_DECODE); + ok = xdr_replymsg(&reply_xdrs, &reply_msg); + /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ + if (ok) { + sunrpc_seterr_reply(&reply_msg, &(cu->cu_error)); + if (cu->cu_error.re_status == RPC_SUCCESS) { + if (! AUTH_VALIDATE(cl->cl_auth, + &reply_msg.acpted_rply.ar_verf)) { + cu->cu_error.re_status = RPC_AUTHERROR; + cu->cu_error.re_why = AUTH_INVALIDRESP; + } else if (! AUTH_UNWRAP(cl->cl_auth, &reply_xdrs, + xresults, resultsp)) { + if (cu->cu_error.re_status == RPC_SUCCESS) + cu->cu_error.re_status = RPC_CANTDECODERES; + } + } /* end successful completion */ + else { + /* maybe our credentials need to be refreshed ... */ + if (nrefreshes > 0 && + AUTH_REFRESH(cl->cl_auth, &reply_msg)) { + nrefreshes--; + goto call_again; + } + } /* end of unsuccessful completion */ + /* free verifier */ + if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && + (reply_msg.acpted_rply.ar_verf.oa_base != NULL)) { + xdrs->x_op = XDR_FREE; + (void)xdr_opaque_auth(xdrs, + &(reply_msg.acpted_rply.ar_verf)); + } + } /* end of valid reply message */ + else { + cu->cu_error.re_status = RPC_CANTDECODERES; + } + return (cu->cu_error.re_status); +} + +static void +clntudp_geterr(cl, errp) + CLIENT *cl; + struct rpc_err *errp; +{ + register struct cu_data *cu = (struct cu_data *)cl->cl_private; + + *errp = cu->cu_error; +} + + +static bool_t +clntudp_freeres(cl, xdr_res, res_ptr) + CLIENT *cl; + xdrproc_t xdr_res; + caddr_t res_ptr; +{ + register struct cu_data *cu = (struct cu_data *)cl->cl_private; + register XDR *xdrs = &(cu->cu_outxdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_res)(xdrs, res_ptr)); +} + +static void +clntudp_abort(/*h*/) + /*CLIENT *h;*/ +{ +} + +static bool_t +clntudp_control(cl, request, info) + CLIENT *cl; + int request; + char *info; +{ + register struct cu_data *cu = (struct cu_data *)cl->cl_private; + int len; + + switch (request) { + case CLSET_TIMEOUT: + cu->cu_total = *(struct timeval *)info; + break; + case CLGET_TIMEOUT: + *(struct timeval *)info = cu->cu_total; + break; + case CLSET_RETRY_TIMEOUT: + cu->cu_wait = *(struct timeval *)info; + break; + case CLGET_RETRY_TIMEOUT: + *(struct timeval *)info = cu->cu_wait; + break; + case CLGET_SERVER_ADDR: + *(struct sockaddr_in *)info = cu->cu_raddr; + break; + case CLGET_LOCAL_ADDR: + len = sizeof(struct sockaddr); + if (getsockname(cu->cu_sock, (struct sockaddr*)info, &len) < 0) + return FALSE; + else + return TRUE; + default: + return (FALSE); + } + return (TRUE); +} + +static void +clntudp_destroy(cl) + CLIENT *cl; +{ + register struct cu_data *cu = (struct cu_data *)cl->cl_private; + + if (cu->cu_closeit) { + (void)close(cu->cu_sock); + } + XDR_DESTROY(&(cu->cu_outxdrs)); + mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz)); + mem_free((caddr_t)cl, sizeof(CLIENT)); +} diff --git a/src/lib/rpc/configure.in b/src/lib/rpc/configure.in new file mode 100644 index 000000000..6084638d3 --- /dev/null +++ b/src/lib/rpc/configure.in @@ -0,0 +1,41 @@ +AC_INIT(auth_gssapi.c) +CONFIG_RULES +AC_PROG_ARCHIVE +AC_PROG_ARCHIVE_ADD +AC_PROG_RANLIB +AC_PROG_INSTALL + +AC_CHECK_SIZEOF(int) +SIZEOF_INT=$ac_cv_sizeof_int +AC_SUBST(SIZEOF_INT) +AC_CHECK_SIZEOF(long) +SIZEOF_LONG=$ac_cv_sizeof_long +AC_SUBST(SIZEOF_LONG) + +DECLARE_SYS_ERRLIST + +V5_SHARED_LIB_OBJS +V5_MAKE_SHARED_LIB(libgssrpc,1.0,.., ./rpc) +GSSAPI_KRB5_SH_VERS=$krb5_cv_shlib_version_libgssapi_krb5 +AC_SUBST(GSSAPI_KRB5_SH_VERS) +KRB5_SH_VERS=$krb5_cv_shlib_version_libkrb5 +AC_SUBST(KRB5_SH_VERS) +CRYPTO_SH_VERS=$krb5_cv_shlib_version_libcrypto +AC_SUBST(CRYPTO_SH_VERS) +COMERR_SH_VERS=$krb5_cv_shlib_version_libcom_err +AC_SUBST(COMERR_SH_VERS) +CopySrcHeader(auth.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(auth_gssapi.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(auth_unix.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(clnt.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(netdb.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(pmap_clnt.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(pmap_prot.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(pmap_rmt.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(rpc.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(rpc_msg.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(svc.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(svc_auth.h,[$](BUILDTOP)/include/rpc) +CopyHeader(types.h,[$](BUILDTOP)/include/rpc) +CopySrcHeader(xdr.h,[$](BUILDTOP)/include/rpc) +V5_AC_OUTPUT_MAKEFILE( ,types.h:types.hin) diff --git a/src/lib/rpc/get_myaddress.c b/src/lib/rpc/get_myaddress.c new file mode 100644 index 000000000..fa4c54e78 --- /dev/null +++ b/src/lib/rpc/get_myaddress.c @@ -0,0 +1,95 @@ +/* @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * get_myaddress.c + * + * Get client's IP address via ioctl. This avoids using the yellowpages. + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#if defined(sun) +#include +#endif +#include +#ifdef OSF1 +#include +#include +#endif +#include +#include +#include +#include + +/* + * don't use gethostbyname, which would invoke yellow pages + */ +get_myaddress(addr) + struct sockaddr_in *addr; +{ + int s; + char buf[BUFSIZ]; + struct ifconf ifc; + struct ifreq ifreq, *ifr; + int len; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("get_myaddress: socket"); + exit(1); + } + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { + perror("get_myaddress: ioctl (get interface configuration)"); + exit(1); + } + ifr = ifc.ifc_req; + for (len = ifc.ifc_len; len; len -= sizeof ifreq) { + ifreq = *ifr; + if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + perror("get_myaddress: ioctl"); + exit(1); + } + if ((ifreq.ifr_flags & IFF_UP) && + ifr->ifr_addr.sa_family == AF_INET) { + *addr = *((struct sockaddr_in *)&ifr->ifr_addr); + addr->sin_port = htons(PMAPPORT); + break; + } + ifr++; + } + (void) close(s); +} diff --git a/src/lib/rpc/getrpcent.c b/src/lib/rpc/getrpcent.c new file mode 100644 index 000000000..e48d5b1e9 --- /dev/null +++ b/src/lib/rpc/getrpcent.c @@ -0,0 +1,256 @@ +/* @(#)getrpcent.c 2.2 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)getrpcent.c 1.9 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1985 by Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* setrpcent is declared as int-returning in netdb.h on hpux */ +/* setrpcent is declared as int-returning in rpc/rpcent.h on Solaris */ +#if !defined(hpux) && !(defined(sun) && defined(__svr4__)) +#define SETRPCENT_TYPE void +#else +#define SETRPCENT_TYPE int +#endif + +/* endrpcent is declared as int-returning in netdb.h on hpux */ +#ifndef hpux +#define ENDRPCENT_TYPE void +#else +#define ENDRPCENT_TYPE int +#endif + +SETRPCENT_TYPE setrpcent(int); +ENDRPCENT_TYPE endrpcent(void); + +/* + * Internet version. + */ +struct rpcdata { + FILE *rpcf; + char *current; + int currentlen; + int stayopen; +#define MAXALIASES 35 + char *rpc_aliases[MAXALIASES]; + struct rpcent rpc; + char line[BUFSIZ+1]; + char *domain; +} *rpcdata; +static struct rpcdata *_rpcdata(); + +static struct rpcent *interpret(); +struct hostent *gethostent(); +char *inet_ntoa(); + +static char RPCDB[] = "/etc/rpc"; + +static struct rpcdata * +_rpcdata() +{ + register struct rpcdata *d = rpcdata; + + if (d == 0) { + d = (struct rpcdata *)calloc(1, sizeof (struct rpcdata)); + rpcdata = d; + } + return (d); +} + +struct rpcent * +getrpcbynumber(number) + register int number; +{ + register struct rpcdata *d = _rpcdata(); + register struct rpcent *p; + int reason; + char adrstr[16], *val = NULL; + int vallen; + + if (d == 0) + return (0); + setrpcent(0); + while (p = getrpcent()) { + if (p->r_number == number) + break; + } + endrpcent(); + return (p); +} + +struct rpcent * +getrpcbyname(name) + const char *name; +{ + struct rpcent *rpc; + char **rp; + + setrpcent(0); + while(rpc = getrpcent()) { + if (strcmp(rpc->r_name, name) == 0) + return (rpc); + for (rp = rpc->r_aliases; *rp != NULL; rp++) { + if (strcmp(*rp, name) == 0) + return (rpc); + } + } + endrpcent(); + return (NULL); +} + +SETRPCENT_TYPE setrpcent(f) + int f; +{ + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return; + if (d->rpcf == NULL) + d->rpcf = fopen(RPCDB, "r"); + else + rewind(d->rpcf); + if (d->current) + free(d->current); + d->current = NULL; + d->stayopen |= f; +} + +ENDRPCENT_TYPE endrpcent() +{ + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return; + if (d->current && !d->stayopen) { + free(d->current); + d->current = NULL; + } + if (d->rpcf && !d->stayopen) { + fclose(d->rpcf); + d->rpcf = NULL; + } +} + +struct rpcent * +getrpcent() +{ + struct rpcent *hp; + int reason; + char *key = NULL, *val = NULL; + int keylen, vallen; + register struct rpcdata *d = _rpcdata(); + + if (d == 0) + return(NULL); + if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL) + return (NULL); + if (fgets(d->line, BUFSIZ, d->rpcf) == NULL) + return (NULL); + return interpret(d->line, strlen(d->line)); +} + +static struct rpcent * +interpret(val, len) +char *val; +{ + register struct rpcdata *d = _rpcdata(); + char *p; + register char *cp, **q; + + if (d == 0) + return; + strncpy(d->line, val, len); + p = d->line; + d->line[len] = '\n'; + if (*p == '#') + return (getrpcent()); + cp = strchr(p, '#'); + if (cp == NULL) + { + cp = strchr(p, '\n'); + if (cp == NULL) + return (getrpcent()); + } + *cp = '\0'; + cp = strchr(p, ' '); + if (cp == NULL) + { + cp = strchr(p, '\t'); + if (cp == NULL) + return (getrpcent()); + } + *cp++ = '\0'; + /* THIS STUFF IS INTERNET SPECIFIC */ + d->rpc.r_name = d->line; + while (*cp == ' ' || *cp == '\t') + cp++; + d->rpc.r_number = atoi(cp); + q = d->rpc.r_aliases = d->rpc_aliases; + cp = strchr(p, ' '); + if (cp != NULL) + *cp++ = '\0'; + else + { + cp = strchr(p, '\t'); + if (cp != NULL) + *cp++ = '\0'; + } + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &(d->rpc_aliases[MAXALIASES - 1])) + *q++ = cp; + cp = strchr(p, ' '); + if (cp != NULL) + *cp++ = '\0'; + else + { + cp = strchr(p, '\t'); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + return (&d->rpc); +} diff --git a/src/lib/rpc/getrpcport.c b/src/lib/rpc/getrpcport.c new file mode 100644 index 000000000..d209a1527 --- /dev/null +++ b/src/lib/rpc/getrpcport.c @@ -0,0 +1,55 @@ +/* @(#)getrpcport.c 2.1 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)getrpcport.c 1.3 87/08/11 SMI"; +#endif +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Copyright (c) 1985 by Sun Microsystems, Inc. + */ + +#include +#include +#include +#include + +getrpcport(host, prognum, versnum, proto) + char *host; +{ + struct sockaddr_in addr; + struct hostent *hp; + + if ((hp = gethostbyname(host)) == NULL) + return (0); + memmove((char *) &addr.sin_addr, hp->h_addr, hp->h_length); + addr.sin_family = AF_INET; + addr.sin_port = 0; + return (pmap_getport(&addr, prognum, versnum, proto)); +} diff --git a/src/lib/rpc/netdb.h b/src/lib/rpc/netdb.h new file mode 100644 index 000000000..f6b6374b5 --- /dev/null +++ b/src/lib/rpc/netdb.h @@ -0,0 +1,50 @@ +#ifndef RPC_NETDB_H +#define RPC_NETDB_H + +/* @(#)netdb.h 2.1 88/07/29 3.9 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)rpc.h 1.8 87/07/24 SMI */ + +/* since the gssrpc library requires that any application using it be +built with these header files, I am making the decision that any app +which uses the rpcent routines must use this header file, or something +compatible (which most are) --marc */ + +/* Really belongs in */ + +struct rpcent { + char *r_name; /* name of server for this rpc program */ + char **r_aliases; /* alias list */ + int r_number; /* rpc program number */ +}; + +struct rpcent *getrpcbyname(), *getrpcbynumber(), *getrpcent(); + +#endif diff --git a/src/lib/rpc/pmap_clnt.c b/src/lib/rpc/pmap_clnt.c new file mode 100644 index 000000000..7218777cc --- /dev/null +++ b/src/lib/rpc/pmap_clnt.c @@ -0,0 +1,115 @@ +/* @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_clnt.c + * Client interface to pmap rpc service. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include + +static struct timeval timeout = { 5, 0 }; +static struct timeval tottimeout = { 60, 0 }; + +void clnt_perror(); + + +/* + * Set a mapping between program,version and port. + * Calls the pmap service remotely to do the mapping. + */ +bool_t +pmap_set(program, version, protocol, port) + rpc_u_int32 program; + rpc_u_int32 version; + int protocol; + unsigned short port; +{ + struct sockaddr_in myaddress; + int socket = -1; + register CLIENT *client; + struct pmap parms; + bool_t rslt; + + get_myaddress(&myaddress); + client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, + timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client == (CLIENT *)NULL) + return (FALSE); + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_prot = protocol; + parms.pm_port = port; + if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt, + tottimeout) != RPC_SUCCESS) { + clnt_perror(client, "Cannot register service"); + return (FALSE); + } + CLNT_DESTROY(client); + (void)close(socket); + return (rslt); +} + +/* + * Remove the mapping between program,version and port. + * Calls the pmap service remotely to do the un-mapping. + */ +bool_t +pmap_unset(program, version) + rpc_u_int32 program; + rpc_u_int32 version; +{ + struct sockaddr_in myaddress; + int socket = -1; + register CLIENT *client; + struct pmap parms; + bool_t rslt; + + get_myaddress(&myaddress); + client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS, + timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client == (CLIENT *)NULL) + return (FALSE); + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_port = parms.pm_prot = 0; + CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt, + tottimeout); + CLNT_DESTROY(client); + (void)close(socket); + return (rslt); +} diff --git a/src/lib/rpc/pmap_clnt.h b/src/lib/rpc/pmap_clnt.h new file mode 100644 index 000000000..dfb00dcc5 --- /dev/null +++ b/src/lib/rpc/pmap_clnt.h @@ -0,0 +1,65 @@ +/* @(#)pmap_clnt.h 2.1 88/07/29 4.0 RPCSRC; from 1.11 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * pmap_clnt.h + * Supplies C routines to get to portmap services. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +/* + * Usage: + * success = pmap_set(program, version, protocol, port); + * success = pmap_unset(program, version); + * port = pmap_getport(address, program, version, protocol); + * head = pmap_getmaps(address); + * clnt_stat = pmap_rmtcall(address, program, version, procedure, + * xdrargs, argsp, xdrres, resp, tout, port_ptr) + * (works for udp only.) + * clnt_stat = clnt_broadcast(program, version, procedure, + * xdrargs, argsp, xdrres, resp, eachresult) + * (like pmap_rmtcall, except the call is broadcasted to all + * locally connected nets. For each valid response received, + * the procedure eachresult is called. Its form is: + * done = eachresult(resp, raddr) + * bool_t done; + * caddr_t resp; + * struct sockaddr_in raddr; + * where resp points to the results of the call and raddr is the + * address if the responder to the broadcast. + */ + +extern bool_t pmap_set(); +extern bool_t pmap_unset(); +extern struct pmaplist *pmap_getmaps(); +enum clnt_stat pmap_rmtcall(); +enum clnt_stat clnt_broadcast(); +extern unsigned short pmap_getport(); diff --git a/src/lib/rpc/pmap_getmaps.c b/src/lib/rpc/pmap_getmaps.c new file mode 100644 index 000000000..472b2a46d --- /dev/null +++ b/src/lib/rpc/pmap_getmaps.c @@ -0,0 +1,88 @@ +/* @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_getmap.c + * Client interface to pmap rpc service. + * contains pmap_getmaps, which is only tcp service involved + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef OSF1 +#include +#include +#endif +#include +#include +#define NAMELEN 255 +#define MAX_BROADCAST_SIZE 1400 + +extern int errno; + +/* + * Get a copy of the current port maps. + * Calls the pmap service remotely to do get the maps. + */ +struct pmaplist * +pmap_getmaps(address) + struct sockaddr_in *address; +{ + struct pmaplist *head = (struct pmaplist *)NULL; + int socket = -1; + struct timeval minutetimeout; + register CLIENT *client; + + minutetimeout.tv_sec = 60; + minutetimeout.tv_usec = 0; + address->sin_port = htons(PMAPPORT); + client = clnttcp_create(address, PMAPPROG, + PMAPVERS, &socket, 50, 500); + if (client != (CLIENT *)NULL) { + if (CLNT_CALL(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist, + &head, minutetimeout) != RPC_SUCCESS) { + clnt_perror(client, "pmap_getmaps rpc problem"); + } + CLNT_DESTROY(client); + } + (void)close(socket); + address->sin_port = 0; + return (head); +} diff --git a/src/lib/rpc/pmap_getport.c b/src/lib/rpc/pmap_getport.c new file mode 100644 index 000000000..40b764880 --- /dev/null +++ b/src/lib/rpc/pmap_getport.c @@ -0,0 +1,91 @@ +/* @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_getport.c + * Client interface to pmap rpc service. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#ifdef OSF1 +#include +#include +#endif +#include + +static struct timeval timeout = { 5, 0 }; +static struct timeval tottimeout = { 60, 0 }; + +/* + * Find the mapped port for program,version. + * Calls the pmap service remotely to do the lookup. + * Returns 0 if no map exists. + */ +unsigned short +pmap_getport(address, program, version, protocol) + struct sockaddr_in *address; + rpc_u_int32 program; + rpc_u_int32 version; + unsigned int protocol; +{ + unsigned short port = 0; + int socket = -1; + register CLIENT *client; + struct pmap parms; + + address->sin_port = htons(PMAPPORT); + client = clntudp_bufcreate(address, PMAPPROG, + PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE); + if (client != (CLIENT *)NULL) { + parms.pm_prog = program; + parms.pm_vers = version; + parms.pm_prot = protocol; + parms.pm_port = 0; /* not needed or used */ + if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms, + xdr_u_short, &port, tottimeout) != RPC_SUCCESS){ + rpc_createerr.cf_stat = RPC_PMAPFAILURE; + clnt_geterr(client, &rpc_createerr.cf_error); + } else if (port == 0) { + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + } + CLNT_DESTROY(client); + } + (void)close(socket); + address->sin_port = 0; + return (port); +} diff --git a/src/lib/rpc/pmap_prot.c b/src/lib/rpc/pmap_prot.c new file mode 100644 index 000000000..1dffffe17 --- /dev/null +++ b/src/lib/rpc/pmap_prot.c @@ -0,0 +1,57 @@ +/* @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_prot.c + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include + + +bool_t +xdr_pmap(xdrs, regs) + XDR *xdrs; + struct pmap *regs; +{ + + if (xdr_u_int32(xdrs, ®s->pm_prog) && + xdr_u_int32(xdrs, ®s->pm_vers) && + xdr_u_int32(xdrs, ®s->pm_prot)) + return (xdr_u_int32(xdrs, ®s->pm_port)); + return (FALSE); +} diff --git a/src/lib/rpc/pmap_prot.h b/src/lib/rpc/pmap_prot.h new file mode 100644 index 000000000..4f76580f4 --- /dev/null +++ b/src/lib/rpc/pmap_prot.h @@ -0,0 +1,94 @@ +/* @(#)pmap_prot.h 2.1 88/07/29 4.0 RPCSRC; from 1.14 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * pmap_prot.h + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * The following procedures are supported by the protocol: + * + * PMAPPROC_NULL() returns () + * takes nothing, returns nothing + * + * PMAPPROC_SET(struct pmap) returns (bool_t) + * TRUE is success, FALSE is failure. Registers the tuple + * [prog, vers, prot, port]. + * + * PMAPPROC_UNSET(struct pmap) returns (bool_t) + * TRUE is success, FALSE is failure. Un-registers pair + * [prog, vers]. prot and port are ignored. + * + * PMAPPROC_GETPORT(struct pmap) returns (rpc_int32 unsigned). + * 0 is failure. Otherwise returns the port number where the pair + * [prog, vers] is registered. It may lie! + * + * PMAPPROC_DUMP() RETURNS (struct pmaplist *) + * + * PMAPPROC_CALLIT(unsigned, unsigned, unsigned, string<>) + * RETURNS (port, string<>); + * usage: encapsulatedresults = PMAPPROC_CALLIT(prog, vers, proc, encapsulatedargs); + * Calls the procedure on the local machine. If it is not registered, + * this procedure is quite; ie it does not return error information!!! + * This procedure only is supported on rpc/udp and calls via + * rpc/udp. This routine only passes null authentication parameters. + * This file has no interface to xdr routines for PMAPPROC_CALLIT. + * + * The service supports remote procedure calls on udp/ip or tcp/ip socket 111. + */ + +#define PMAPPORT ((unsigned short)111) +#define PMAPPROG ((rpc_u_int32)100000) +#define PMAPVERS ((rpc_u_int32)2) +#define PMAPVERS_PROTO ((rpc_u_int32)2) +#define PMAPVERS_ORIG ((rpc_u_int32)1) +#define PMAPPROC_NULL ((rpc_u_int32)0) +#define PMAPPROC_SET ((rpc_u_int32)1) +#define PMAPPROC_UNSET ((rpc_u_int32)2) +#define PMAPPROC_GETPORT ((rpc_u_int32)3) +#define PMAPPROC_DUMP ((rpc_u_int32)4) +#define PMAPPROC_CALLIT ((rpc_u_int32)5) + +struct pmap { + rpc_u_int32 pm_prog; + rpc_u_int32 pm_vers; + rpc_u_int32 pm_prot; + rpc_u_int32 pm_port; +}; + +extern bool_t xdr_pmap(); + +struct pmaplist { + struct pmap pml_map; + struct pmaplist *pml_next; +}; + +extern bool_t xdr_pmaplist(); diff --git a/src/lib/rpc/pmap_prot2.c b/src/lib/rpc/pmap_prot2.c new file mode 100644 index 000000000..5f48f2a28 --- /dev/null +++ b/src/lib/rpc/pmap_prot2.c @@ -0,0 +1,116 @@ +/* @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_prot2.c + * Protocol for the local binder service, or pmap. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include + + +/* + * What is going on with linked lists? (!) + * First recall the link list declaration from pmap_prot.h: + * + * struct pmaplist { + * struct pmap pml_map; + * struct pmaplist *pml_map; + * }; + * + * Compare that declaration with a corresponding xdr declaration that + * is (a) pointer-less, and (b) recursive: + * + * typedef union switch (bool_t) { + * + * case TRUE: struct { + * struct pmap; + * pmaplist_t foo; + * }; + * + * case FALSE: struct {}; + * } pmaplist_t; + * + * Notice that the xdr declaration has no nxt pointer while + * the C declaration has no bool_t variable. The bool_t can be + * interpreted as ``more data follows me''; if FALSE then nothing + * follows this bool_t; if TRUE then the bool_t is followed by + * an actual struct pmap, and then (recursively) by the + * xdr union, pamplist_t. + * + * This could be implemented via the xdr_union primitive, though this + * would cause a one recursive call per element in the list. Rather than do + * that we can ``unwind'' the recursion + * into a while loop and do the union arms in-place. + * + * The head of the list is what the C programmer wishes to past around + * the net, yet is the data that the pointer points to which is interesting; + * this sounds like a job for xdr_reference! + */ +bool_t +xdr_pmaplist(xdrs, rp) + register XDR *xdrs; + register struct pmaplist **rp; +{ + /* + * more_elements is pre-computed in case the direction is + * XDR_ENCODE or XDR_FREE. more_elements is overwritten by + * xdr_bool when the direction is XDR_DECODE. + */ + bool_t more_elements; + register int freeing = (xdrs->x_op == XDR_FREE); + register struct pmaplist **next; + + while (TRUE) { + more_elements = (bool_t)(*rp != NULL); + if (! xdr_bool(xdrs, &more_elements)) + return (FALSE); + if (! more_elements) + return (TRUE); /* we are done */ + /* + * the unfortunate side effect of non-recursion is that in + * the case of freeing we must remember the next object + * before we free the current object ... + */ + if (freeing) + next = &((*rp)->pml_next); + if (! xdr_reference(xdrs, (caddr_t *)rp, + (unsigned int)sizeof(struct pmaplist), xdr_pmap)) + return (FALSE); + rp = (freeing) ? next : &((*rp)->pml_next); + } +} diff --git a/src/lib/rpc/pmap_rmt.c b/src/lib/rpc/pmap_rmt.c new file mode 100644 index 000000000..6a2376763 --- /dev/null +++ b/src/lib/rpc/pmap_rmt.c @@ -0,0 +1,403 @@ +/* @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro"; +#endif + +/* + * pmap_rmt.c + * Client interface to pmap rpc service. + * remote call and broadcast service + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include +#include +#ifdef sparc +#include +#endif +#include +#include +#ifdef OSF1 +#include +#include +#endif +#include +#include +#include +#define MAX_BROADCAST_SIZE 1400 + +extern int errno; +static struct timeval timeout = { 3, 0 }; + + +/* + * pmapper remote-call-service interface. + * This routine is used to call the pmapper remote call service + * which will look up a service program in the port maps, and then + * remotely call that routine with the given parameters. This allows + * programs to do a lookup and call in one step. +*/ +enum clnt_stat +pmap_rmtcall(addr, prog, vers, proc, xdrargs, argsp, xdrres, resp, tout, port_ptr) + struct sockaddr_in *addr; + rpc_u_int32 prog, vers, proc; + xdrproc_t xdrargs, xdrres; + caddr_t argsp, resp; + struct timeval tout; + rpc_u_int32 *port_ptr; +{ + int socket = -1; + register CLIENT *client; + struct rmtcallargs a; + struct rmtcallres r; + enum clnt_stat stat; + + addr->sin_port = htons(PMAPPORT); + client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket); + if (client != (CLIENT *)NULL) { + a.prog = prog; + a.vers = vers; + a.proc = proc; + a.args_ptr = argsp; + a.xdr_args = xdrargs; + r.port_ptr = port_ptr; + r.results_ptr = resp; + r.xdr_results = xdrres; + stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a, + xdr_rmtcallres, &r, tout); + CLNT_DESTROY(client); + } else { + stat = RPC_FAILED; + } + (void)close(socket); + addr->sin_port = 0; + return (stat); +} + + +/* + * XDR remote call arguments + * written for XDR_ENCODE direction only + */ +bool_t +xdr_rmtcall_args(xdrs, cap) + register XDR *xdrs; + register struct rmtcallargs *cap; +{ + unsigned int lenposition, argposition, position; + + if (xdr_u_int32(xdrs, &(cap->prog)) && + xdr_u_int32(xdrs, &(cap->vers)) && + xdr_u_int32(xdrs, &(cap->proc))) { + lenposition = XDR_GETPOS(xdrs); + if (! xdr_u_int32(xdrs, &(cap->arglen))) + return (FALSE); + argposition = XDR_GETPOS(xdrs); + if (! (*(cap->xdr_args))(xdrs, cap->args_ptr)) + return (FALSE); + position = XDR_GETPOS(xdrs); + cap->arglen = (rpc_u_int32)position - (rpc_u_int32)argposition; + XDR_SETPOS(xdrs, lenposition); + if (! xdr_u_int32(xdrs, &(cap->arglen))) + return (FALSE); + XDR_SETPOS(xdrs, position); + return (TRUE); + } + return (FALSE); +} + +/* + * XDR remote call results + * written for XDR_DECODE direction only + */ +bool_t +xdr_rmtcallres(xdrs, crp) + register XDR *xdrs; + register struct rmtcallres *crp; +{ + caddr_t port_ptr; + + port_ptr = (caddr_t)crp->port_ptr; + if (xdr_reference(xdrs, &port_ptr, sizeof (rpc_u_int32), + xdr_u_int32) && xdr_u_int32(xdrs, &crp->resultslen)) { + crp->port_ptr = (rpc_u_int32 *)port_ptr; + return ((*(crp->xdr_results))(xdrs, crp->results_ptr)); + } + return (FALSE); +} + + +/* + * The following is kludged-up support for simple rpc broadcasts. + * Someday a large, complicated system will replace these trivial + * routines which only support udp/ip . + */ + +static int +getbroadcastnets(addrs, sock, buf) + struct in_addr *addrs; + int sock; /* any valid socket will do */ + char *buf; /* why allocxate more when we can use existing... */ +{ + struct ifconf ifc; + struct ifreq ifreq, *ifr; + struct sockaddr_in *sin; + int n, i; + + ifc.ifc_len = UDPMSGSIZE; + ifc.ifc_buf = buf; + if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { + perror("broadcast: ioctl (get interface configuration)"); + return (0); + } + ifr = ifc.ifc_req; + for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) { + ifreq = *ifr; + if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) { + perror("broadcast: ioctl (get interface flags)"); + continue; + } + if ((ifreq.ifr_flags & IFF_BROADCAST) && + (ifreq.ifr_flags & IFF_UP) && + ifr->ifr_addr.sa_family == AF_INET) { + sin = (struct sockaddr_in *)&ifr->ifr_addr; +#ifdef SIOCGIFBRDADDR /* 4.3BSD */ + if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { + addrs[i++].s_addr = INADDR_ANY; +#if 0 /* this is uuuuugly */ + addrs[i++] = inet_makeaddr(inet_netof +#if defined(hpux) || (defined(sparc) && defined(__svr4__)) || defined(linux) || (defined(__osf__) && defined(__alpha__)) + (sin->sin_addr), INADDR_ANY); +#else /* hpux or solaris */ + (sin->sin_addr.s_addr), INADDR_ANY); +#endif +#endif + } else { + addrs[i++] = ((struct sockaddr_in*) + &ifreq.ifr_addr)->sin_addr; + } +#else /* 4.2 BSD */ + addrs[i++] = inet_makeaddr(inet_netof + (sin->sin_addr.s_addr), INADDR_ANY); +#endif + } + } + return (i); +} + +typedef bool_t (*resultproc_t)(); + +enum clnt_stat +clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult) + rpc_u_int32 prog; /* program number */ + rpc_u_int32 vers; /* version number */ + rpc_u_int32 proc; /* procedure number */ + xdrproc_t xargs; /* xdr routine for args */ + caddr_t argsp; /* pointer to args */ + xdrproc_t xresults; /* xdr routine for results */ + caddr_t resultsp; /* pointer to results */ + resultproc_t eachresult; /* call with each result obtained */ +{ + enum clnt_stat stat; + AUTH *unix_auth = authunix_create_default(); + XDR xdr_stream; + register XDR *xdrs = &xdr_stream; + int outlen, inlen, fromlen, nets; + register int sock; + int on = 1; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; +#else + int readfds; + register int mask; +#endif /* def FD_SETSIZE */ + register int i; + bool_t done = FALSE; + register rpc_u_int32 xid; + rpc_u_int32 port; + struct in_addr addrs[20]; + struct sockaddr_in baddr, raddr; /* broadcast and response addresses */ + struct rmtcallargs a; + struct rmtcallres r; + struct rpc_msg msg; + struct timeval t; + char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE]; + + /* + * initialization: create a socket, a broadcast address, and + * preserialize the arguments into a send buffer. + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("Cannot create socket for broadcast rpc"); + stat = RPC_CANTSEND; + goto done_broad; + } +#ifdef SO_BROADCAST + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &on, + sizeof (on)) < 0) { + perror("Cannot set socket option SO_BROADCAST"); + stat = RPC_CANTSEND; + goto done_broad; + } +#endif /* def SO_BROADCAST */ +#ifdef FD_SETSIZE + FD_ZERO(&mask); + FD_SET(sock, &mask); +#else + mask = (1 << sock); +#endif /* def FD_SETSIZE */ + nets = getbroadcastnets(addrs, sock, inbuf); + bzero((char *)&baddr, sizeof (baddr)); + baddr.sin_family = AF_INET; + baddr.sin_port = htons(PMAPPORT); + baddr.sin_addr.s_addr = htonl(INADDR_ANY); +/* baddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); */ + (void)gettimeofday(&t, (struct timezone *)0); + msg.rm_xid = xid = getpid() ^ t.tv_sec ^ t.tv_usec; + t.tv_usec = 0; + msg.rm_direction = CALL; + msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + msg.rm_call.cb_prog = PMAPPROG; + msg.rm_call.cb_vers = PMAPVERS; + msg.rm_call.cb_proc = PMAPPROC_CALLIT; + msg.rm_call.cb_cred = unix_auth->ah_cred; + msg.rm_call.cb_verf = unix_auth->ah_verf; + a.prog = prog; + a.vers = vers; + a.proc = proc; + a.xdr_args = xargs; + a.args_ptr = argsp; + r.port_ptr = &port; + r.xdr_results = xresults; + r.results_ptr = resultsp; + xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE); + if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) { + stat = RPC_CANTENCODEARGS; + goto done_broad; + } + outlen = (int)xdr_getpos(xdrs); + xdr_destroy(xdrs); + /* + * Basic loop: broadcast a packet and wait a while for response(s). + * The response timeout grows larger per iteration. + */ + for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) { + for (i = 0; i < nets; i++) { + baddr.sin_addr = addrs[i]; + if (sendto(sock, outbuf, outlen, 0, + (struct sockaddr *)&baddr, + sizeof (struct sockaddr)) != outlen) { + perror("Cannot send broadcast packet"); + stat = RPC_CANTSEND; + goto done_broad; + } + } + if (eachresult == NULL) { + stat = RPC_SUCCESS; + goto done_broad; + } + recv_again: + msg.acpted_rply.ar_verf = _null_auth; + msg.acpted_rply.ar_results.where = (caddr_t)&r; + msg.acpted_rply.ar_results.proc = xdr_rmtcallres; + readfds = mask; + switch (select(_rpc_dtablesize(), &readfds, (fd_set *)NULL, + (fd_set *)NULL, &t)) { + + case 0: /* timed out */ + stat = RPC_TIMEDOUT; + continue; + + case -1: /* some kind of error */ + if (errno == EINTR) + goto recv_again; + perror("Broadcast select problem"); + stat = RPC_CANTRECV; + goto done_broad; + + } /* end of select results switch */ + try_again: + fromlen = sizeof(struct sockaddr); + inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0, + (struct sockaddr *)&raddr, &fromlen); + if (inlen < 0) { + if (errno == EINTR) + goto try_again; + perror("Cannot receive reply to broadcast"); + stat = RPC_CANTRECV; + goto done_broad; + } + if (inlen < sizeof(rpc_u_int32)) + goto recv_again; + /* + * see if reply transaction id matches sent id. + * If so, decode the results. + */ + xdrmem_create(xdrs, inbuf, (unsigned int)inlen, XDR_DECODE); + if (xdr_replymsg(xdrs, &msg)) { + if ((msg.rm_xid == xid) && + (msg.rm_reply.rp_stat == MSG_ACCEPTED) && + (msg.acpted_rply.ar_stat == SUCCESS)) { + raddr.sin_port = htons((unsigned short)port); + done = (*eachresult)(resultsp, &raddr); + } + /* otherwise, we just ignore the errors ... */ + } else { +#ifdef notdef + /* some kind of deserialization problem ... */ + if (msg.rm_xid == xid) + fprintf(stderr, "Broadcast deserialization problem"); + /* otherwise, just random garbage */ +#endif + } + xdrs->x_op = XDR_FREE; + msg.acpted_rply.ar_results.proc = xdr_void; + (void)xdr_replymsg(xdrs, &msg); + (void)(*xresults)(xdrs, resultsp); + xdr_destroy(xdrs); + if (done) { + stat = RPC_SUCCESS; + goto done_broad; + } else { + goto recv_again; + } + } +done_broad: + (void)close(sock); + AUTH_DESTROY(unix_auth); + return (stat); +} + diff --git a/src/lib/rpc/pmap_rmt.h b/src/lib/rpc/pmap_rmt.h new file mode 100644 index 000000000..2ea22120e --- /dev/null +++ b/src/lib/rpc/pmap_rmt.h @@ -0,0 +1,53 @@ +/* @(#)pmap_rmt.h 2.1 88/07/29 4.0 RPCSRC; from 1.2 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * Structures and XDR routines for parameters to and replies from + * the portmapper remote-call-service. + * + * Copyright (C) 1986, Sun Microsystems, Inc. + */ + +struct rmtcallargs { + rpc_u_int32 prog, vers, proc, arglen; + caddr_t args_ptr; + xdrproc_t xdr_args; +}; + +bool_t xdr_rmtcall_args(); + +struct rmtcallres { + rpc_u_int32 *port_ptr; + rpc_u_int32 resultslen; + caddr_t results_ptr; + xdrproc_t xdr_results; +}; + +bool_t xdr_rmtcallres(); diff --git a/src/lib/rpc/rpc.h b/src/lib/rpc/rpc.h new file mode 100644 index 000000000..d0280aaa0 --- /dev/null +++ b/src/lib/rpc/rpc.h @@ -0,0 +1,74 @@ +/* @(#)rpc.h 2.3 88/08/10 4.0 RPCSRC; from 1.9 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * rpc.h, Just includes the billions of rpc header files necessary to + * do remote procedure calling. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ +#ifndef __RPC_HEADER__ +#define __RPC_HEADER__ + +#include /* some typedefs */ +#include + +/* external data representation interfaces */ +#include /* generic (de)serializer */ + +/* Client side only authentication */ +#include /* generic authenticator (client side) */ + +/* Client side (mostly) remote procedure call */ +#include /* generic rpc stuff */ + +/* semi-private protocol headers */ +#include /* protocol for rpc messages */ +#include /* protocol for unix style cred */ +/* + * Uncomment-out the next line if you are building the rpc library with + * DES Authentication (see the README file in the secure_rpc/ directory). + */ +/*#include protocol for des style cred */ + +/* Server side only remote procedure callee */ +#include /* service side authenticator */ +#include /* service manager and multiplexer */ + +/* + * COMMENT OUT THE NEXT INCLUDE IF RUNNING ON SUN OS OR ON A VERSION + * OF UNIX BASED ON NFSSRC. These systems will already have the structures + * defined by included in . + */ +/* routines for parsing /etc/rpc */ +#include +#include /* structures and routines to parse /etc/rpc */ + +#endif /* ndef __RPC_HEADER__ */ diff --git a/src/lib/rpc/rpc_callmsg.c b/src/lib/rpc/rpc_callmsg.c new file mode 100644 index 000000000..370e79ff9 --- /dev/null +++ b/src/lib/rpc/rpc_callmsg.c @@ -0,0 +1,195 @@ +/* @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * rpc_callmsg.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + */ + +#include + +#include + +/* + * XDR a call message + */ +bool_t +xdr_callmsg(xdrs, cmsg) + register XDR *xdrs; + register struct rpc_msg *cmsg; +{ + register rpc_int32 *buf; + register struct opaque_auth *oa; + + if (xdrs->x_op == XDR_ENCODE) { + if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + buf = (rpc_int32 *) XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT + + RNDUP(cmsg->rm_call.cb_cred.oa_length) + + 2 * BYTES_PER_XDR_UNIT + + RNDUP(cmsg->rm_call.cb_verf.oa_length)); + if (buf != NULL) { + IXDR_PUT_LONG(buf, cmsg->rm_xid); + IXDR_PUT_ENUM(buf, cmsg->rm_direction); + if (cmsg->rm_direction != CALL) { + return (FALSE); + } + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_rpcvers); + if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) { + return (FALSE); + } + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_prog); + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_vers); + IXDR_PUT_LONG(buf, cmsg->rm_call.cb_proc); + oa = &cmsg->rm_call.cb_cred; + IXDR_PUT_ENUM(buf, oa->oa_flavor); + IXDR_PUT_LONG(buf, oa->oa_length); + if (oa->oa_length) { + memmove((caddr_t)buf, oa->oa_base, + oa->oa_length); + buf += RNDUP(oa->oa_length) / sizeof (rpc_int32); + } + oa = &cmsg->rm_call.cb_verf; + IXDR_PUT_ENUM(buf, oa->oa_flavor); + IXDR_PUT_LONG(buf, oa->oa_length); + if (oa->oa_length) { + memmove((caddr_t)buf, oa->oa_base, + oa->oa_length); + /* no real need.... + buf += RNDUP(oa->oa_length) / sizeof (rpc_int32); + */ + } + return (TRUE); + } + } + if (xdrs->x_op == XDR_DECODE) { + buf = (rpc_int32 *) XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT); + if (buf != NULL) { + cmsg->rm_xid = IXDR_GET_LONG(buf); + cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type); + if (cmsg->rm_direction != CALL) { + return (FALSE); + } + cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG(buf); + if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) { + return (FALSE); + } + cmsg->rm_call.cb_prog = IXDR_GET_LONG(buf); + cmsg->rm_call.cb_vers = IXDR_GET_LONG(buf); + cmsg->rm_call.cb_proc = IXDR_GET_LONG(buf); + oa = &cmsg->rm_call.cb_cred; + oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); + oa->oa_length = IXDR_GET_LONG(buf); + if (oa->oa_length) { + if (oa->oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (oa->oa_base == NULL) { + oa->oa_base = (caddr_t) + mem_alloc(oa->oa_length); + } + buf = (rpc_int32 *) + XDR_INLINE(xdrs, RNDUP(oa->oa_length)); + if (buf == NULL) { + if (xdr_opaque(xdrs, oa->oa_base, + oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + memmove(oa->oa_base, (caddr_t)buf, + oa->oa_length); + /* no real need.... + buf += RNDUP(oa->oa_length) / + sizeof (rpc_int32); + */ + } + } + oa = &cmsg->rm_call.cb_verf; + buf = (rpc_int32 *) + XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE || + xdr_u_int(xdrs, &oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); + oa->oa_length = IXDR_GET_LONG(buf); + } + if (oa->oa_length) { + if (oa->oa_length > MAX_AUTH_BYTES) { + return (FALSE); + } + if (oa->oa_base == NULL) { + oa->oa_base = (caddr_t) + mem_alloc(oa->oa_length); + } + buf = (rpc_int32 *) + XDR_INLINE(xdrs, RNDUP(oa->oa_length)); + if (buf == NULL) { + if (xdr_opaque(xdrs, oa->oa_base, + oa->oa_length) == FALSE) { + return (FALSE); + } + } else { + memmove(oa->oa_base, (caddr_t) buf, + oa->oa_length); + /* no real need... + buf += RNDUP(oa->oa_length) / + sizeof (rpc_int32); + */ + } + } + return (TRUE); + } + } + if ( + xdr_u_int32(xdrs, &(cmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) && + (cmsg->rm_direction == CALL) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_rpcvers)) && + (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_prog)) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_vers)) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_proc)) && + xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_cred)) ) + return (xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_verf))); + return (FALSE); +} + diff --git a/src/lib/rpc/rpc_commondata.c b/src/lib/rpc/rpc_commondata.c new file mode 100644 index 000000000..75cead087 --- /dev/null +++ b/src/lib/rpc/rpc_commondata.c @@ -0,0 +1,41 @@ +/* @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#include +/* + * This file should only contain common data (global data) that is exported + * by public interfaces + */ +struct opaque_auth _null_auth; +#ifdef FD_SETSIZE +fd_set svc_fdset; +#else +int svc_fds; +#endif /* def FD_SETSIZE */ +struct rpc_createerr rpc_createerr; diff --git a/src/lib/rpc/rpc_dtablesize.c b/src/lib/rpc/rpc_dtablesize.c new file mode 100644 index 000000000..d252d6acd --- /dev/null +++ b/src/lib/rpc/rpc_dtablesize.c @@ -0,0 +1,60 @@ +/* @(#)rpc_dtablesize.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_dtablesize.c 1.2 87/08/11 Copyr 1987 Sun Micro"; +#endif + +#include + +/* + * Cache the result of getdtablesize(), so we don't have to do an + * expensive system call every time. + */ +_rpc_dtablesize() +{ + static int size; + + if (size == 0) { +#ifdef _SC_OPEN_MAX + size = (int) sysconf(_SC_OPEN_MAX); +#else + size = getdtablesize(); +#endif + +/* sysconf() can return a number larger than what will fit in an + fd_set. we can't use fd's larger than this, anyway. */ + +#ifdef FD_SETSIZE + if (size >= FD_SETSIZE) + size = FD_SETSIZE-1; +#endif + } + return (size); +} diff --git a/src/lib/rpc/rpc_msg.h b/src/lib/rpc/rpc_msg.h new file mode 100644 index 000000000..66b10c5fb --- /dev/null +++ b/src/lib/rpc/rpc_msg.h @@ -0,0 +1,187 @@ +/* @(#)rpc_msg.h 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)rpc_msg.h 1.7 86/07/16 SMI */ + +/* + * rpc_msg.h + * rpc message definition + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#define RPC_MSG_VERSION ((rpc_u_int32) 2) +#define RPC_SERVICE_PORT ((unsigned short) 2048) + +/* + * Bottom up definition of an rpc message. + * NOTE: call and reply use the same overall stuct but + * different parts of unions within it. + */ + +enum msg_type { + CALL=0, + REPLY=1 +}; + +enum reply_stat { + MSG_ACCEPTED=0, + MSG_DENIED=1 +}; + +enum accept_stat { + SUCCESS=0, + PROG_UNAVAIL=1, + PROG_MISMATCH=2, + PROC_UNAVAIL=3, + GARBAGE_ARGS=4, + SYSTEM_ERR=5 +}; + +enum reject_stat { + RPC_MISMATCH=0, + AUTH_ERROR=1 +}; + +/* + * Reply part of an rpc exchange + */ + +/* + * Reply to an rpc request that was accepted by the server. + * Note: there could be an error even though the request was + * accepted. + */ +struct accepted_reply { + struct opaque_auth ar_verf; + enum accept_stat ar_stat; + union { + struct { + rpc_u_int32 low; + rpc_u_int32 high; + } AR_versions; + struct { + caddr_t where; + xdrproc_t proc; + } AR_results; + /* and many other null cases */ + } ru; +#define ar_results ru.AR_results +#define ar_vers ru.AR_versions +}; + +/* + * Reply to an rpc request that was rejected by the server. + */ +struct rejected_reply { + enum reject_stat rj_stat; + union { + struct { + rpc_u_int32 low; + rpc_u_int32 high; + } RJ_versions; + enum auth_stat RJ_why; /* why authentication did not work */ + } ru; +#define rj_vers ru.RJ_versions +#define rj_why ru.RJ_why +}; + +/* + * Body of a reply to an rpc request. + */ +struct reply_body { + enum reply_stat rp_stat; + union { + struct accepted_reply RP_ar; + struct rejected_reply RP_dr; + } ru; +#define rp_acpt ru.RP_ar +#define rp_rjct ru.RP_dr +}; + +/* + * Body of an rpc request call. + */ +struct call_body { + rpc_u_int32 cb_rpcvers; /* must be equal to two */ + rpc_u_int32 cb_prog; + rpc_u_int32 cb_vers; + rpc_u_int32 cb_proc; + struct opaque_auth cb_cred; + struct opaque_auth cb_verf; /* protocol specific - provided by client */ +}; + +/* + * The rpc message + */ +struct rpc_msg { + rpc_u_int32 rm_xid; + enum msg_type rm_direction; + union { + struct call_body RM_cmb; + struct reply_body RM_rmb; + } ru; +#define rm_call ru.RM_cmb +#define rm_reply ru.RM_rmb +}; +#define acpted_rply ru.RM_rmb.ru.RP_ar +#define rjcted_rply ru.RM_rmb.ru.RP_dr + + +/* + * XDR routine to handle a rpc message. + * xdr_callmsg(xdrs, cmsg) + * XDR *xdrs; + * struct rpc_msg *cmsg; + */ +extern bool_t xdr_callmsg(); + +/* + * XDR routine to pre-serialize the static part of a rpc message. + * xdr_callhdr(xdrs, cmsg) + * XDR *xdrs; + * struct rpc_msg *cmsg; + */ +extern bool_t xdr_callhdr(); + +/* + * XDR routine to handle a rpc reply. + * xdr_replymsg(xdrs, rmsg) + * XDR *xdrs; + * struct rpc_msg *rmsg; + */ +extern bool_t xdr_replymsg(); + +/* + * Fills in the error part of a reply message. + * _seterr_reply(msg, error) + * struct rpc_msg *msg; + * struct rpc_err *error; + */ +extern void _seterr_reply(); diff --git a/src/lib/rpc/rpc_prot.c b/src/lib/rpc/rpc_prot.c new file mode 100644 index 000000000..6fea605e6 --- /dev/null +++ b/src/lib/rpc/rpc_prot.c @@ -0,0 +1,287 @@ +/* @(#)rpc_prot.c 2.3 88/08/07 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)rpc_prot.c 1.36 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * rpc_prot.c + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * This set of routines implements the rpc message definition, + * its serializer and some common rpc utility routines. + * The routines are meant for various implementations of rpc - + * they are NOT for the rpc client or rpc service implementations! + * Because authentication stuff is easy and is part of rpc, the opaque + * routines are also in this program. + */ + +#include + +#include + +/* * * * * * * * * * * * * * XDR Authentication * * * * * * * * * * * */ + +/* + * XDR an opaque authentication struct + * (see auth.h) + */ +bool_t +xdr_opaque_auth(xdrs, ap) + register XDR *xdrs; + register struct opaque_auth *ap; +{ + + if (xdr_enum(xdrs, &(ap->oa_flavor))) + return (xdr_bytes(xdrs, &ap->oa_base, + &ap->oa_length, MAX_AUTH_BYTES)); + return (FALSE); +} + +/* + * XDR a DES block + */ +bool_t +xdr_des_block(xdrs, blkp) + register XDR *xdrs; + register des_block *blkp; +{ + return (xdr_opaque(xdrs, (caddr_t)blkp, sizeof(des_block))); +} + +/* * * * * * * * * * * * * * XDR RPC MESSAGE * * * * * * * * * * * * * * * */ + +/* + * XDR the MSG_ACCEPTED part of a reply message union + */ +bool_t +xdr_accepted_reply(xdrs, ar) + register XDR *xdrs; + register struct accepted_reply *ar; +{ + + /* personalized union, rather than calling xdr_union */ + if (! xdr_opaque_auth(xdrs, &(ar->ar_verf))) + return (FALSE); + if (! xdr_enum(xdrs, (enum_t *)&(ar->ar_stat))) + return (FALSE); + switch (ar->ar_stat) { + + case SUCCESS: + return ((*(ar->ar_results.proc))(xdrs, ar->ar_results.where)); + + case PROG_MISMATCH: + if (! xdr_u_int32(xdrs, &(ar->ar_vers.low))) + return (FALSE); + return (xdr_u_int32(xdrs, &(ar->ar_vers.high))); + } + return (TRUE); /* TRUE => open ended set of problems */ +} + +/* + * XDR the MSG_DENIED part of a reply message union + */ +bool_t +xdr_rejected_reply(xdrs, rr) + register XDR *xdrs; + register struct rejected_reply *rr; +{ + + /* personalized union, rather than calling xdr_union */ + if (! xdr_enum(xdrs, (enum_t *)&(rr->rj_stat))) + return (FALSE); + switch (rr->rj_stat) { + + case RPC_MISMATCH: + if (! xdr_u_int32(xdrs, &(rr->rj_vers.low))) + return (FALSE); + return (xdr_u_int32(xdrs, &(rr->rj_vers.high))); + + case AUTH_ERROR: + return (xdr_enum(xdrs, (enum_t *)&(rr->rj_why))); + } + return (FALSE); +} + +static struct xdr_discrim reply_dscrm[3] = { + { (int)MSG_ACCEPTED, xdr_accepted_reply }, + { (int)MSG_DENIED, xdr_rejected_reply }, + { __dontcare__, NULL_xdrproc_t } }; + +/* + * XDR a reply message + */ +bool_t +xdr_replymsg(xdrs, rmsg) + register XDR *xdrs; + register struct rpc_msg *rmsg; +{ + if ( + xdr_u_int32(xdrs, &(rmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(rmsg->rm_direction)) && + (rmsg->rm_direction == REPLY) ) + return (xdr_union(xdrs, (enum_t *)&(rmsg->rm_reply.rp_stat), + (caddr_t)&(rmsg->rm_reply.ru), reply_dscrm, NULL_xdrproc_t)); + return (FALSE); +} + + +/* + * Serializes the "static part" of a call message header. + * The fields include: rm_xid, rm_direction, rpcvers, prog, and vers. + * The rm_xid is not really static, but the user can easily munge on the fly. + */ +bool_t +xdr_callhdr(xdrs, cmsg) + register XDR *xdrs; + register struct rpc_msg *cmsg; +{ + + cmsg->rm_direction = CALL; + cmsg->rm_call.cb_rpcvers = RPC_MSG_VERSION; + if ( + (xdrs->x_op == XDR_ENCODE) && + xdr_u_int32(xdrs, &(cmsg->rm_xid)) && + xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_rpcvers)) && + xdr_u_int32(xdrs, &(cmsg->rm_call.cb_prog)) ) + return (xdr_u_int32(xdrs, &(cmsg->rm_call.cb_vers))); + return (FALSE); +} + +/* ************************** Client utility routine ************* */ + +static void +accepted(acpt_stat, error) + register enum accept_stat acpt_stat; + register struct rpc_err *error; +{ + + switch (acpt_stat) { + + case PROG_UNAVAIL: + error->re_status = RPC_PROGUNAVAIL; + return; + + case PROG_MISMATCH: + error->re_status = RPC_PROGVERSMISMATCH; + return; + + case PROC_UNAVAIL: + error->re_status = RPC_PROCUNAVAIL; + return; + + case GARBAGE_ARGS: + error->re_status = RPC_CANTDECODEARGS; + return; + + case SYSTEM_ERR: + error->re_status = RPC_SYSTEMERROR; + return; + + case SUCCESS: + error->re_status = RPC_SUCCESS; + return; + } + /* something's wrong, but we don't know what ... */ + error->re_status = RPC_FAILED; + error->re_lb.s1 = (rpc_int32)MSG_ACCEPTED; + error->re_lb.s2 = (rpc_int32)acpt_stat; +} + +static void +rejected(rjct_stat, error) + register enum reject_stat rjct_stat; + register struct rpc_err *error; +{ + + switch (rjct_stat) { + + case RPC_VERSMISMATCH: + error->re_status = RPC_VERSMISMATCH; + return; + + case AUTH_ERROR: + error->re_status = RPC_AUTHERROR; + return; + } + /* something's wrong, but we don't know what ... */ + error->re_status = RPC_FAILED; + error->re_lb.s1 = (rpc_int32)MSG_DENIED; + error->re_lb.s2 = (rpc_int32)rjct_stat; +} + +/* + * given a reply message, fills in the error + */ +void +sunrpc_seterr_reply(msg, error) + register struct rpc_msg *msg; + register struct rpc_err *error; +{ + + /* optimized for normal, SUCCESSful case */ + switch (msg->rm_reply.rp_stat) { + + case MSG_ACCEPTED: + if (msg->acpted_rply.ar_stat == SUCCESS) { + error->re_status = RPC_SUCCESS; + return; + }; + accepted(msg->acpted_rply.ar_stat, error); + break; + + case MSG_DENIED: + rejected(msg->rjcted_rply.rj_stat, error); + break; + + default: + error->re_status = RPC_FAILED; + error->re_lb.s1 = (rpc_int32)(msg->rm_reply.rp_stat); + break; + } + switch (error->re_status) { + + case RPC_VERSMISMATCH: + error->re_vers.low = msg->rjcted_rply.rj_vers.low; + error->re_vers.high = msg->rjcted_rply.rj_vers.high; + break; + + case RPC_AUTHERROR: + error->re_why = msg->rjcted_rply.rj_why; + break; + + case RPC_PROGVERSMISMATCH: + error->re_vers.low = msg->acpted_rply.ar_vers.low; + error->re_vers.high = msg->acpted_rply.ar_vers.high; + break; + } +} diff --git a/src/lib/rpc/svc.c b/src/lib/rpc/svc.c new file mode 100644 index 000000000..cb4d877a7 --- /dev/null +++ b/src/lib/rpc/svc.c @@ -0,0 +1,492 @@ +/* @(#)svc.c 2.4 88/08/11 4.0 RPCSRC; from 1.44 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc.c 1.41 87/10/13 Copyr 1984 Sun Micro"; +#endif + +/* + * svc.c, Server-side remote procedure call interface. + * + * There are two sets of procedures here. The xprt routines are + * for handling transport handles. The svc routines handle the + * list of service routines. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include + +extern int errno; + +#ifdef FD_SETSIZE +static SVCXPRT **xports; +#else +#define NOFILE 32 + +static SVCXPRT *xports[NOFILE]; +#endif /* def FD_SETSIZE */ + +#define NULL_SVC ((struct svc_callout *)0) +#define RQCRED_SIZE 400 /* this size is excessive */ + +/* + * The services list + * Each entry represents a set of procedures (an rpc program). + * The dispatch routine takes request structs and runs the + * apropriate procedure. + */ +static struct svc_callout { + struct svc_callout *sc_next; + rpc_u_int32 sc_prog; + rpc_u_int32 sc_vers; + void (*sc_dispatch)(); +} *svc_head; + +static struct svc_callout *svc_find(); + +/* *************** SVCXPRT related stuff **************** */ + +/* + * Activate a transport handle. + */ +void +xprt_register(xprt) + SVCXPRT *xprt; +{ + register int sock = xprt->xp_sock; + +#ifdef FD_SETSIZE + if (xports == NULL) { + xports = (SVCXPRT **) + mem_alloc(FD_SETSIZE * sizeof(SVCXPRT *)); + } + if (sock < _rpc_dtablesize()) { + xports[sock] = xprt; + FD_SET(sock, &svc_fdset); + } +#else + if (sock < NOFILE) { + xports[sock] = xprt; + svc_fds |= (1 << sock); + } +#endif /* def FD_SETSIZE */ + +} + +/* + * De-activate a transport handle. + */ +void +xprt_unregister(xprt) + SVCXPRT *xprt; +{ + register int sock = xprt->xp_sock; + +#ifdef FD_SETSIZE + if ((sock < _rpc_dtablesize()) && (xports[sock] == xprt)) { + xports[sock] = (SVCXPRT *)0; + FD_CLR(sock, &svc_fdset); + } +#else + if ((sock < NOFILE) && (xports[sock] == xprt)) { + xports[sock] = (SVCXPRT *)0; + svc_fds &= ~(1 << sock); + } +#endif /* def FD_SETSIZE */ +} + + +/* ********************** CALLOUT list related stuff ************* */ + +/* + * Add a service program to the callout list. + * The dispatch routine will be called when a rpc request for this + * program number comes in. + */ +bool_t +svc_register(xprt, prog, vers, dispatch, protocol) + SVCXPRT *xprt; + rpc_u_int32 prog; + rpc_u_int32 vers; + void (*dispatch)(); + int protocol; +{ + struct svc_callout *prev; + register struct svc_callout *s; + + if ((s = svc_find(prog, vers, &prev)) != NULL_SVC) { + if (s->sc_dispatch == dispatch) + goto pmap_it; /* he is registering another xptr */ + return (FALSE); + } + s = (struct svc_callout *)mem_alloc(sizeof(struct svc_callout)); + if (s == (struct svc_callout *)0) { + return (FALSE); + } + s->sc_prog = prog; + s->sc_vers = vers; + s->sc_dispatch = dispatch; + s->sc_next = svc_head; + svc_head = s; +pmap_it: + /* now register the information with the local binder service */ + if (protocol) { + return (pmap_set(prog, vers, protocol, xprt->xp_port)); + } + return (TRUE); +} + +/* + * Remove a service program from the callout list. + */ +void +svc_unregister(prog, vers) + rpc_u_int32 prog; + rpc_u_int32 vers; +{ + struct svc_callout *prev; + register struct svc_callout *s; + + if ((s = svc_find(prog, vers, &prev)) == NULL_SVC) + return; + if (prev == NULL_SVC) { + svc_head = s->sc_next; + } else { + prev->sc_next = s->sc_next; + } + s->sc_next = NULL_SVC; + mem_free((char *) s, (unsigned int) sizeof(struct svc_callout)); + /* now unregister the information with the local binder service */ + (void)pmap_unset(prog, vers); +} + +/* + * Search the callout list for a program number, return the callout + * struct. + */ +static struct svc_callout * +svc_find(prog, vers, prev) + rpc_u_int32 prog; + rpc_u_int32 vers; + struct svc_callout **prev; +{ + register struct svc_callout *s, *p; + + p = NULL_SVC; + for (s = svc_head; s != NULL_SVC; s = s->sc_next) { + if ((s->sc_prog == prog) && (s->sc_vers == vers)) + goto done; + p = s; + } +done: + *prev = p; + return (s); +} + +/* ******************* REPLY GENERATION ROUTINES ************ */ + +/* + * Send a reply to an rpc request + */ +bool_t +svc_sendreply(xprt, xdr_results, xdr_location) + register SVCXPRT *xprt; + xdrproc_t xdr_results; + caddr_t xdr_location; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = SUCCESS; + rply.acpted_rply.ar_results.where = xdr_location; + rply.acpted_rply.ar_results.proc = xdr_results; + return (SVC_REPLY(xprt, &rply)); +} + +/* + * No procedure error reply + */ +void +svcerr_noproc(xprt) + register SVCXPRT *xprt; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROC_UNAVAIL; + SVC_REPLY(xprt, &rply); +} + +/* + * Can't decode args error reply + */ +void +svcerr_decode(xprt) + register SVCXPRT *xprt; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = GARBAGE_ARGS; + SVC_REPLY(xprt, &rply); +} + +/* + * Some system error + */ +void +svcerr_systemerr(xprt) + register SVCXPRT *xprt; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = SYSTEM_ERR; + SVC_REPLY(xprt, &rply); +} + +/* + * Authentication error reply + */ +void +svcerr_auth(xprt, why) + SVCXPRT *xprt; + enum auth_stat why; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_DENIED; + rply.rjcted_rply.rj_stat = AUTH_ERROR; + rply.rjcted_rply.rj_why = why; + SVC_REPLY(xprt, &rply); +} + +/* + * Auth too weak error reply + */ +void +svcerr_weakauth(xprt) + SVCXPRT *xprt; +{ + + svcerr_auth(xprt, AUTH_TOOWEAK); +} + +/* + * Program unavailable error reply + */ +void +svcerr_noprog(xprt) + register SVCXPRT *xprt; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROG_UNAVAIL; + SVC_REPLY(xprt, &rply); +} + +/* + * Program version mismatch error reply + */ +void +svcerr_progvers(xprt, low_vers, high_vers) + register SVCXPRT *xprt; + rpc_u_int32 low_vers; + rpc_u_int32 high_vers; +{ + struct rpc_msg rply; + + rply.rm_direction = REPLY; + rply.rm_reply.rp_stat = MSG_ACCEPTED; + rply.acpted_rply.ar_verf = xprt->xp_verf; + rply.acpted_rply.ar_stat = PROG_MISMATCH; + rply.acpted_rply.ar_vers.low = low_vers; + rply.acpted_rply.ar_vers.high = high_vers; + SVC_REPLY(xprt, &rply); +} + +/* ******************* SERVER INPUT STUFF ******************* */ + +/* + * Get server side input from some transport. + * + * Statement of authentication parameters management: + * This function owns and manages all authentication parameters, specifically + * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and + * the "cooked" credentials (rqst->rq_clntcred). + * However, this function does not know the structure of the cooked + * credentials, so it make the following assumptions: + * a) the structure is contiguous (no pointers), and + * b) the cred structure size does not exceed RQCRED_SIZE bytes. + * In all events, all three parameters are freed upon exit from this routine. + * The storage is trivially management on the call stack in user land, but + * is mallocated in kernel land. + */ + +void +svc_getreq(rdfds) + int rdfds; +{ +#ifdef FD_SETSIZE + fd_set readfds; + + FD_ZERO(&readfds); + readfds.fds_bits[0] = rdfds; + svc_getreqset(&readfds); +#else + int readfds = rdfds & svc_fds; + + svc_getreqset(&readfds); +#endif /* def FD_SETSIZE */ +} + +void +svc_getreqset(readfds) +#ifdef FD_SETSIZE + fd_set *readfds; +{ +#else + int *readfds; +{ + int readfds_local = *readfds; +#endif /* def FD_SETSIZE */ + enum xprt_stat stat; + struct rpc_msg msg; + int prog_found; + rpc_u_int32 low_vers; + rpc_u_int32 high_vers; + struct svc_req r; + register SVCXPRT *xprt; + rpc_u_int32 mask; + int bit; + rpc_u_int32 *maskp; + register int setsize; + register int sock; + bool_t no_dispatch; + + char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE]; + msg.rm_call.cb_cred.oa_base = cred_area; + msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]); + r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]); + +#ifdef FD_SETSIZE + setsize = _rpc_dtablesize(); + + maskp = (rpc_u_int32 *)readfds->fds_bits; + for (sock = 0; sock < setsize; sock += NFDBITS) { + for (mask = *maskp++; bit = ffs(mask); mask ^= (1 << (bit - 1))) { + /* sock has input waiting */ + xprt = xports[sock + bit - 1]; +#else + for (sock = 0; readfds_local != 0; sock++, readfds_local >>= 1) { + if ((readfds_local & 1) != 0) { + /* sock has input waiting */ + xprt = xports[sock]; +#endif /* def FD_SETSIZE */ + /* now receive msgs from xprtprt (support batch calls) */ + do { + if (SVC_RECV(xprt, &msg)) { + + /* now find the exported program and call it */ + register struct svc_callout *s; + enum auth_stat why; + + r.rq_xprt = xprt; + r.rq_prog = msg.rm_call.cb_prog; + r.rq_vers = msg.rm_call.cb_vers; + r.rq_proc = msg.rm_call.cb_proc; + r.rq_cred = msg.rm_call.cb_cred; + + /* in case _authenticate has been replaced + with an old-style version */ + r.rq_xprt->xp_auth = &svc_auth_any; + no_dispatch = FALSE; + + /* first authenticate the message */ + why=_authenticate(&r, &msg, &no_dispatch); + if (why != AUTH_OK) { + svcerr_auth(xprt, why); + goto call_done; + } else if (no_dispatch) { + goto call_done; + } + + /* now match message with a registered service*/ + prog_found = FALSE; + low_vers = 0 - 1; + high_vers = 0; + for (s = svc_head; s != NULL_SVC; s = s->sc_next) { + if (s->sc_prog == r.rq_prog) { + if (s->sc_vers == r.rq_vers) { + (*s->sc_dispatch)(&r, xprt); + goto call_done; + } /* found correct version */ + prog_found = TRUE; + if (s->sc_vers < low_vers) + low_vers = s->sc_vers; + if (s->sc_vers > high_vers) + high_vers = s->sc_vers; + } /* found correct program */ + } + /* + * if we got here, the program or version + * is not served ... + */ + if (prog_found) + svcerr_progvers(xprt, + low_vers, high_vers); + else + svcerr_noprog(xprt); + /* Fall through to ... */ + } + call_done: + if ((stat = SVC_STAT(xprt)) == XPRT_DIED){ + SVC_DESTROY(xprt); + break; + } + } while (stat == XPRT_MOREREQS); + } + } +} diff --git a/src/lib/rpc/svc.h b/src/lib/rpc/svc.h new file mode 100644 index 000000000..2114d6249 --- /dev/null +++ b/src/lib/rpc/svc.h @@ -0,0 +1,298 @@ +/* @(#)svc.h 2.2 88/07/29 4.0 RPCSRC; from 1.20 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc.h, Server-side remote procedure call interface. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __SVC_HEADER__ +#define __SVC_HEADER__ + +/* + * This interface must manage two items concerning remote procedure calling: + * + * 1) An arbitrary number of transport connections upon which rpc requests + * are received. The two most notable transports are TCP and UDP; they are + * created and registered by routines in svc_tcp.c and svc_udp.c, respectively; + * they in turn call xprt_register and xprt_unregister. + * + * 2) An arbitrary number of locally registered services. Services are + * described by the following four data: program number, version number, + * "service dispatch" function, a transport handle, and a boolean that + * indicates whether or not the exported program should be registered with a + * local binder service; if true the program's number and version and the + * port number from the transport handle are registered with the binder. + * These data are registered with the rpc svc system via svc_register. + * + * A service's dispatch function is called whenever an rpc request comes in + * on a transport. The request's program and version numbers must match + * those of the registered service. The dispatch function is passed two + * parameters, struct svc_req * and SVCXPRT *, defined below. + */ + +enum xprt_stat { + XPRT_DIED, + XPRT_MOREREQS, + XPRT_IDLE +}; + +/* + * Server side transport handle + */ +typedef struct { + int xp_sock; + unsigned short xp_port; /* associated port number */ + struct xp_ops { + bool_t (*xp_recv)(); /* receive incomming requests */ + enum xprt_stat (*xp_stat)(); /* get transport status */ + bool_t (*xp_getargs)(); /* get arguments */ + bool_t (*xp_reply)(); /* send reply */ + bool_t (*xp_freeargs)();/* free mem allocated for args */ + void (*xp_destroy)(); /* destroy this struct */ + } *xp_ops; + int xp_addrlen; /* length of remote address */ + struct sockaddr_in xp_raddr; /* remote address */ + struct opaque_auth xp_verf; /* raw response verifier */ + SVCAUTH *xp_auth; /* auth flavor of current req */ + caddr_t xp_p1; /* private */ + caddr_t xp_p2; /* private */ +} SVCXPRT; + +/* + * Approved way of getting address of caller + */ +#define svc_getcaller(x) (&(x)->xp_raddr) + +/* + * Operations defined on an SVCXPRT handle + * + * SVCXPRT *xprt; + * struct rpc_msg *msg; + * xdrproc_t xargs; + * caddr_t argsp; + */ +#define SVC_RECV(xprt, msg) \ + (*(xprt)->xp_ops->xp_recv)((xprt), (msg)) +#define svc_recv(xprt, msg) \ + (*(xprt)->xp_ops->xp_recv)((xprt), (msg)) + +#define SVC_STAT(xprt) \ + (*(xprt)->xp_ops->xp_stat)(xprt) +#define svc_stat(xprt) \ + (*(xprt)->xp_ops->xp_stat)(xprt) + +#define SVC_GETARGS(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp)) +#define svc_getargs(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs)((xprt), (xargs), (argsp)) + +#define SVC_GETARGS_REQ(xprt, req, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs_req)((xprt), (req), (xargs), (argsp)) +#define svc_getargs_req(xprt, req, xargs, argsp) \ + (*(xprt)->xp_ops->xp_getargs_req)((xprt), (req), (xargs), (argsp)) + +#define SVC_REPLY(xprt, msg) \ + (*(xprt)->xp_ops->xp_reply) ((xprt), (msg)) +#define svc_reply(xprt, msg) \ + (*(xprt)->xp_ops->xp_reply) ((xprt), (msg)) + +#define SVC_REPLY_REQ(xprt, req, msg) \ + (*(xprt)->xp_ops->xp_reply_req) ((xprt), (req), (msg)) +#define svc_reply_req(xprt, msg) \ + (*(xprt)->xp_ops->xp_reply_req) ((xprt), (req), (msg)) + +#define SVC_FREEARGS(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp)) +#define svc_freeargs(xprt, xargs, argsp) \ + (*(xprt)->xp_ops->xp_freeargs)((xprt), (xargs), (argsp)) + +#define SVC_DESTROY(xprt) \ + (*(xprt)->xp_ops->xp_destroy)(xprt) +#define svc_destroy(xprt) \ + (*(xprt)->xp_ops->xp_destroy)(xprt) + + +/* + * Service request + */ +struct svc_req { + rpc_u_int32 rq_prog; /* service program number */ + rpc_u_int32 rq_vers; /* service protocol version */ + rpc_u_int32 rq_proc; /* the desired procedure */ + struct opaque_auth rq_cred; /* raw creds from the wire */ + caddr_t rq_clntcred; /* read only cooked client cred */ + caddr_t rq_svccred; /* read only cooked svc cred */ + SVCXPRT *rq_xprt; /* associated transport */ + + /* The request's auth flavor *should* be here, but the svc_req */ + /* isn't passed around everywhere it is necessary. The */ + /* transport *is* passed around, so the auth flavor it stored */ + /* there. This means that the transport must be single */ + /* threaded, but other parts of SunRPC already require that. */ + /*SVCAUTH *rq_auth; associated auth flavor */ +}; + + +/* + * Service registration + * + * svc_register(xprt, prog, vers, dispatch, protocol) + * SVCXPRT *xprt; + * rpc_u_int32 prog; + * rpc_u_int32 vers; + * void (*dispatch)(); + * int protocol; like TCP or UDP, zero means do not register + */ +extern bool_t svc_register(); + +/* + * Service un-registration + * + * svc_unregister(prog, vers) + * rpc_u_int32 prog; + * rpc_u_int32 vers; + */ +extern void svc_unregister(); + +/* + * Transport registration. + * + * xprt_register(xprt) + * SVCXPRT *xprt; + */ +extern void xprt_register(); + +/* + * Transport un-register + * + * xprt_unregister(xprt) + * SVCXPRT *xprt; + */ +extern void xprt_unregister(); + + + + +/* + * When the service routine is called, it must first check to see if + * it knows about the procedure; if not, it should call svcerr_noproc + * and return. If so, it should deserialize its arguments via + * SVC_GETARGS or the new SVC_GETARGS_REQ (both defined above). If + * the deserialization does not work, svcerr_decode should be called + * followed by a return. Successful decoding of the arguments should + * be followed the execution of the procedure's code and a call to + * svc_sendreply or the new svc_sendreply_req. + * + * Also, if the service refuses to execute the procedure due to too- + * weak authentication parameters, svcerr_weakauth should be called. + * Note: do not confuse access-control failure with weak authentication! + * + * NB: In pure implementations of rpc, the caller always waits for a reply + * msg. This message is sent when svc_sendreply is called. + * Therefore pure service implementations should always call + * svc_sendreply even if the function logically returns void; use + * xdr.h - xdr_void for the xdr routine. HOWEVER, tcp based rpc allows + * for the abuse of pure rpc via batched calling or pipelining. In the + * case of a batched call, svc_sendreply should NOT be called since + * this would send a return message, which is what batching tries to avoid. + * It is the service/protocol writer's responsibility to know which calls are + * batched and which are not. Warning: responding to batch calls may + * deadlock the caller and server processes! + */ + +extern bool_t svc_sendreply(); +extern void svcerr_decode(); +extern void svcerr_weakauth(); +extern void svcerr_noproc(); +extern void svcerr_progvers(); +extern void svcerr_auth(); +extern void svcerr_noprog(); +extern void svcerr_systemerr(); + +/* + * Lowest level dispatching -OR- who owns this process anyway. + * Somebody has to wait for incoming requests and then call the correct + * service routine. The routine svc_run does infinite waiting; i.e., + * svc_run never returns. + * Since another (co-existant) package may wish to selectively wait for + * incoming calls or other events outside of the rpc architecture, the + * routine svc_getreq is provided. It must be passed readfds, the + * "in-place" results of a select system call (see select, section 2). + */ + +/* + * Global keeper of rpc service descriptors in use + * dynamic; must be inspected before each call to select + */ +#ifdef FD_SETSIZE +extern fd_set svc_fdset; +#define svc_fds svc_fdset.fds_bits[0] /* compatibility */ +#else +extern int svc_fds; +#endif /* def FD_SETSIZE */ + +/* + * a small program implemented by the svc_rpc implementation itself; + * also see clnt.h for protocol numbers. + */ +extern void rpctest_service(); + +extern void svc_getreq(); +extern void svc_getreqset(); /* takes fdset instead of int */ +extern void svc_run(); /* never returns */ + +/* + * Socket to use on svcxxx_create call to get default socket + */ +#define RPC_ANYSOCK -1 + +/* + * These are the existing service side transport implementations + */ + +/* + * Memory based rpc for testing and timing. + */ +extern SVCXPRT *svcraw_create(); + +/* + * Udp based rpc. + */ +extern SVCXPRT *svcudp_create(); +extern SVCXPRT *svcudp_bufcreate(); + +/* + * Tcp based rpc. + */ +extern SVCXPRT *svctcp_create(); + +#endif /* !__SVC_HEADER__ */ diff --git a/src/lib/rpc/svc_auth.c b/src/lib/rpc/svc_auth.c new file mode 100644 index 000000000..281d7cba4 --- /dev/null +++ b/src/lib/rpc/svc_auth.c @@ -0,0 +1,119 @@ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_auth.c 2.1 88/08/07 4.0 RPCSRC; from 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * svc_auth_nodes.c, Server-side rpc authenticator interface, + * *WITHOUT* DES authentication. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include + +/* + * Server side authenticators are called from authenticate by + * using the client auth struct flavor field to index into svcauthsw. + * The server auth flavors must implement a routine that looks + * like: + * + * enum auth_stat + * flavorx_auth(rqst, msg) + * register struct svc_req *rqst; + * register struct rpc_msg *msg; + * + */ + +enum auth_stat _svcauth_null(); /* no authentication */ +enum auth_stat _svcauth_unix(); /* unix style (uid, gids) */ +enum auth_stat _svcauth_short(); /* short hand unix style */ +enum auth_stat _svcauth_gssapi(); /* GSS-API style */ + +static struct svcauthsw_type { + unsigned int flavor; + enum auth_stat (*authenticator)(); +} svcauthsw[] = { + AUTH_GSSAPI, _svcauth_gssapi, /* AUTH_GSSAPI */ + AUTH_NONE, _svcauth_null, /* AUTH_NULL */ + AUTH_GSSAPI_COMPAT, _svcauth_gssapi, /* AUTH_GSSAPI_COMPAT */ + AUTH_UNIX, _svcauth_unix, /* AUTH_UNIX */ + AUTH_SHORT, _svcauth_short, /* AUTH_SHORT */ +}; +static int svcauthnum = sizeof(svcauthsw) / sizeof(struct svcauthsw_type); + +/* + * The call rpc message, msg has been obtained from the wire. The msg contains + * the raw form of credentials and verifiers. authenticate returns AUTH_OK + * if the msg is successfully authenticated. If AUTH_OK then the routine also + * does the following things: + * set rqst->rq_xprt->verf to the appropriate response verifier; + * sets rqst->rq_client_cred to the "cooked" form of the credentials. + * + * NB: rqst->rq_cxprt->verf must be pre-alloctaed; + * its length is set appropriately. + * + * The caller still owns and is responsible for msg->u.cmb.cred and + * msg->u.cmb.verf. The authentication system retains ownership of + * rqst->rq_client_cred, the cooked credentials. + */ +enum auth_stat +_authenticate(rqst, msg, no_dispatch) + register struct svc_req *rqst; + struct rpc_msg *msg; + bool_t *no_dispatch; +{ + register int cred_flavor, i; + + rqst->rq_cred = msg->rm_call.cb_cred; + rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor; + rqst->rq_xprt->xp_verf.oa_length = 0; + cred_flavor = rqst->rq_cred.oa_flavor; + *no_dispatch = FALSE; + for (i = 0; i < svcauthnum; i++) { + if (cred_flavor == svcauthsw[i].flavor && + svcauthsw[i].authenticator != NULL) { + return ((*(svcauthsw[i].authenticator))(rqst, + msg, + no_dispatch)); + } + } + + return (AUTH_REJECTEDCRED); +} + +enum auth_stat +_svcauth_null(rqst, msg) + struct svc_req *rqst; + struct rpc_msg *msg; +{ + rqst->rq_xprt->xp_auth = &svc_auth_any; + return (AUTH_OK); +} diff --git a/src/lib/rpc/svc_auth.h b/src/lib/rpc/svc_auth.h new file mode 100644 index 000000000..4d5963144 --- /dev/null +++ b/src/lib/rpc/svc_auth.h @@ -0,0 +1,61 @@ +/* @(#)svc_auth.h 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)svc_auth.h 1.6 86/07/16 SMI */ + +/* + * svc_auth.h, Service side of rpc authentication. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +/* + * Interface to server-side authentication flavors. + */ +typedef struct { + struct svc_auth_ops { + int (*svc_ah_wrap)(); + int (*svc_ah_unwrap)(); + } *svc_ah_ops; + caddr_t svc_ah_private; +} SVCAUTH; + +extern SVCAUTH svc_auth_any; + +/* + * Server side authenticator + */ +extern enum auth_stat _authenticate(); + +#define SVCAUTH_WRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->svc_ah_ops->svc_ah_wrap))(auth, xdrs, xfunc, xwhere)) +#define SVCAUTH_UNWRAP(auth, xdrs, xfunc, xwhere) \ + ((*((auth)->svc_ah_ops->svc_ah_unwrap))(auth, xdrs, xfunc, xwhere)) + + diff --git a/src/lib/rpc/svc_auth_any.c b/src/lib/rpc/svc_auth_any.c new file mode 100644 index 000000000..2f0a66dc8 --- /dev/null +++ b/src/lib/rpc/svc_auth_any.c @@ -0,0 +1,22 @@ +/* + * svc_auth_any.c + * Provides default service-side functions for authentication flavors + * that do not use all the fields in struct svc_auth_ops. + * + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + */ + +#include +#include + +extern int authany_wrap(); + +struct svc_auth_ops svc_auth_any_ops = { + authany_wrap, + authany_wrap, +}; + +SVCAUTH svc_auth_any = { + &svc_auth_any_ops, + NULL, +}; diff --git a/src/lib/rpc/svc_auth_gssapi.c b/src/lib/rpc/svc_auth_gssapi.c new file mode 100644 index 000000000..07cf59ab8 --- /dev/null +++ b/src/lib/rpc/svc_auth_gssapi.c @@ -0,0 +1,1181 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.37 1996/07/22 20:41:00 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.36.4.1 1996/07/18 04:19:34 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.36.2.1 1996/06/20 23:39:22 marc + * File added to the repository on a branch + * + * Revision 1.36 1996/05/30 19:25:02 bjaspan + * zero bindings structure before using it + * + * Revision 1.35 1996/05/12 06:17:25 marc + * changed around the file includes, since krb5 has changed some. + * + * added conditionalization GSS_BACKWARD_HACK until and if this hack is + * reimplemented in the newly merged gssapi. + * + * conditionalize out the host-specific cruft for setting the local + * address to INADDR_ANY, since you can just assign it that way on all + * platforms I know of. + * + * Revision 1.34 1996/02/12 15:14:00 grier + * [secure/3570] + * restore (struct sockaddr *) cast that got mangled + * + * Revision 1.33 1996/02/07 13:09:52 jik + * Actually, I should have used krb5_error_code, not krb5_int32. + * + * Revision 1.32 1996/02/07 13:08:31 jik + * Include to get the krb5_int32 typedef, which we then use + * in a cast when checking if the GSS-API minor status value is equal to + * a krb5 error code. + * + * Revision 1.31 1996/02/01 18:29:29 grier + * Restore use of error code definition. + * Return original code structure. + * + * Revision 1.30 1996/01/31 19:15:49 grier + * [secure/3570] + * Remove (void *) casts to memcpy() args + * + * Revision 1.29 1996/01/25 03:58:04 grier + * Remove debug code + * + * Revision 1.28 1996/01/25 03:56:50 grier + * secure/3570 - missed Alpha checkin + * + * Revision 1.26 1995/11/07 23:17:23 grier + * memcpy() cast + * + * Revision 1.25 1995/08/24 21:05:48 bjaspan + * set acceptor channel bindings + * + * Revision 1.24 1995/08/23 20:28:02 bjaspan + * [secure-rpc/3392] add channel bindinds to the rpc + * + * Revision 1.23 1995/07/10 18:49:22 bjaspan + * [secure-build/3377] remove use of BSD db + * + * Revision 1.22 1995/05/25 18:35:53 bjaspan + * [secure-rpc/3103] log misc errors from RPC + * + * Revision 1.21 1995/05/24 17:34:03 bjaspan + * [secure-rpc/3302] don't allow client to make server exit unless + * debugging is enabled + * + * Revision 1.20 1995/05/08 22:32:44 marc + * if a new client is in use, set the krb5 gssapi mech into + * backward-compatibility mode. + * + * Revision 1.19 1994/10/27 12:38:51 jik + * [secure-rpc/2808: add credential versioning] + * + * Sandbox: + * + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.22 1994/10/26 20:03:27 bjaspan + * [secure-rpc/2808] add version field to client creds + * + * Revision 1.21 1994/05/23 01:26:01 bjaspan + * [secure-rpc/1911] set rq_svccred to the context instead of the service + * gss name + * + * Revision 1.20 1994/05/09 17:48:39 shanzer + * change sys/fcntl.h to fcntl.h + * + * Revision 1.19 1994/04/08 17:21:32 bjaspan + * remove KRB5KTNAME hack + * + * Revision 1.18 1994/03/18 15:48:13 shanzer + * include sys/fcntl.h + * + * Revision 1.17 1994/03/08 00:05:56 shanzer + * call rand() instead random() + * + * Revision 1.16 1993/12/08 21:43:54 bjaspan + * gss_delete_sec_context failure is not fatal (in fact, the context + * will often be expired); use AUTH_GSSAPI_DISPLAY_STATUS macro + * + * Revision 1.15 1993/12/08 20:20:08 bjaspan + * add debugging info to expire_client, correct comment above btree->put + * + * Revision 1.14 1993/12/08 06:52:49 bjaspan + * *many* debugging improvements to help find secure-rpc/586, and (I hope) + * the fix: don't change client_data->expiration without deleting it + * and reinserting it into the btree + * + * Revision 1.13 1993/12/06 21:22:26 bjaspan + * debugging levels, #ifdef PURIFY, call abort() on impossible failures + * + * Revision 1.12 1993/11/12 02:33:14 bjaspan + * add badauth + * / + * + * Revision 1.11 1993/11/03 23:46:15 bjaspan + * new log_badverf format + * + * Revision 1.10 1993/11/03 21:23:30 bjaspan + * handle GSS_C_INDEFINITE expiration, add log_badverf, set rq_svccred + * + * Revision 1.9 1993/11/03 01:30:36 bjaspan + * don't include gssapi_krb5.h, it isn't needed + * + * Revision 1.8 1993/11/02 22:09:02 bjaspan + * support multiple service-side names via _svcauth_gssapi_set_names + * + * Revision 1.7 1993/11/01 19:56:22 bjaspan + * unstatic svc_debug_gssapi, and send gss_{major,minor} back if + * accept_sec_context fails + * + * Revision 1.6 1993/10/28 22:09:58 bjaspan + * fix verifier mem leak, clean_clients() first to avoid dangling ref, + * only include hacked kt_default_name if DEBUG_GSSAPI defined + * + * Revision 1.5 1993/10/27 18:26:51 bjaspan + * use xdr_free instead of gss_release_buffer; this fixes memory leaks + * that were probably caused by zero-length seal/unseal tokens + * + * Revision 1.4 1993/10/26 21:12:51 bjaspan + * fully working + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* + * svc_auth_gssapi.c + * Handles the GSS-API flavor authentication parameters on the service + * side of RPC. + */ + +#include +#include +#include +#include + +#include +#include + +#ifdef GSS_BACKWARD_HACK +#include +#endif + +/* This is here for the krb5_error_code typedef and the + KRB5KRB_AP_WRONG_PRINC #define.*/ +#include + +#include +#include + +#define INITIATION_TIMEOUT 60*15 /* seconds until partially created */ + /* context is destroed */ +#define INDEF_EXPIRE 60*60*24 /* seconds until an context with no */ + /* expiration time is expired */ + +#ifdef __CODECENTER__ +#define DEBUG_GSSAPI 1 +#endif + +#ifdef DEBUG_GSSAPI +int svc_debug_gssapi = DEBUG_GSSAPI; +#define L_PRINTF(l,args) if (svc_debug_gssapi >= l) printf args +#define PRINTF(args) L_PRINTF(99, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) \ + if (svc_debug_gssapi) auth_gssapi_display_status args +#else +#define PRINTF(args) +#define L_PRINTF(l, args) +#define AUTH_GSSAPI_DISPLAY_STATUS(args) +#endif + +typedef struct _svc_auth_gssapi_data { + bool_t established; + + gss_ctx_id_t context; + gss_name_t client_name, server_name; + gss_cred_id_t server_creds; + + rpc_u_int32 expiration; + rpc_u_int32 seq_num; + rpc_u_int32 key; + + SVCAUTH svcauth; + + /* kludge to free verifiers on next call */ + gss_buffer_desc prev_verf; +} svc_auth_gssapi_data; + +#define SVCAUTH_PRIVATE(auth) \ + ((svc_auth_gssapi_data *)(auth)->svc_ah_private) + +static bool_t svc_auth_gssapi_wrap(); +static bool_t svc_auth_gssapi_unwrap(); +static svc_auth_gssapi_data *create_client(); +static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle); +static void destroy_client(svc_auth_gssapi_data *client_data); +static void clean_client(), cleanup(); +static void client_expire(svc_auth_gssapi_data *client_data, rpc_u_int32 exp); +static void dump_db(char *msg); + +struct svc_auth_ops svc_auth_gssapi_ops = { + svc_auth_gssapi_wrap, + svc_auth_gssapi_unwrap, +}; + +/* + * Globals! Eeek! Run for the hills! + */ +static gss_cred_id_t *server_creds_list = NULL; +static gss_name_t *server_name_list = NULL; +static int server_creds_count = 0; + +static auth_gssapi_log_badauth_func log_badauth = NULL; +static caddr_t log_badauth_data = NULL; +static auth_gssapi_log_badverf_func log_badverf = NULL; +static caddr_t log_badverf_data = NULL; +static auth_gssapi_log_miscerr_func log_miscerr = NULL; +static caddr_t log_miscerr_data = NULL; + +#define LOG_MISCERR(arg) if (log_miscerr) \ + (*log_miscerr)(rqst, msg, arg, log_miscerr_data) + +typedef struct _client_list { + svc_auth_gssapi_data *client; + struct _client_list *next; +} client_list; + +static client_list *clients = NULL; + +extern int errno; + +enum auth_stat _svcauth_gssapi(rqst, msg, no_dispatch) + register struct svc_req *rqst; + register struct rpc_msg *msg; + bool_t *no_dispatch; +{ + XDR xdrs; + auth_gssapi_creds creds; + auth_gssapi_init_arg call_arg; + auth_gssapi_init_res call_res; + gss_buffer_desc output_token, in_buf, out_buf; + gss_cred_id_t server_creds; + struct gss_channel_bindings_struct bindings, *bindp; + struct sockaddr_in sockname; + OM_uint32 gssstat, minor_stat, time_rec; + struct opaque_auth *cred, *verf; + svc_auth_gssapi_data *client_data; + int ret_flags, ret, i; + rpc_u_int32 seq_num; + int flag; + + PRINTF(("svcauth_gssapi: starting\n")); + + /* clean up expired entries */ + clean_client(); + + /* use AUTH_NONE until there is a client_handle */ + rqst->rq_xprt->xp_auth = &svc_auth_any; + + memset((char *) &call_res, 0, sizeof(call_res)); + + cred = &msg->rm_call.cb_cred; + verf = &msg->rm_call.cb_verf; + + if (cred->oa_length == 0) { + PRINTF(("svcauth_gssapi: empty creds, failing\n")); + LOG_MISCERR("empty client credentials"); + ret = AUTH_BADCRED; + goto error; + } + + PRINTF(("svcauth_gssapi: decoding credentials\n")); + xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE); + memset((char *) &creds, 0, sizeof(creds)); + if (! xdr_authgssapi_creds(&xdrs, &creds)) { + PRINTF(("svcauth_gssapi: failed decoding creds\n")); + LOG_MISCERR("protocol error in client credentials"); + XDR_DESTROY(&xdrs); + ret = AUTH_BADCRED; + goto error; + } + XDR_DESTROY(&xdrs); + + PRINTF(("svcauth_gssapi: got credentials, version %d, " + "client_handle len %d\n", creds.version, + creds.client_handle.length)); + + if (creds.version != 2) { + PRINTF(("svcauth_gssapi: bad credential version\n")); + LOG_MISCERR("unsupported client credentials version"); + ret = AUTH_BADCRED; + goto error; + } + +#ifdef DEBUG_GSSAPI + if (svc_debug_gssapi) { + if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) { + PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n")); + svc_sendreply(rqst->rq_xprt, xdr_void, NULL); + xdr_free(xdr_authgssapi_creds, &creds); + cleanup(); + exit(0); + } + } +#endif + + /* + * If this is an auth_msg and proc is GSSAPI_INIT, then create a + * client handle for this client. Otherwise, look up the + * existing handle. + */ + if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) { + if (creds.client_handle.length != 0) { + PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n")); + LOG_MISCERR("protocol error in client handle"); + ret = AUTH_FAILED; + goto error; + } + + PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n")); + + client_data = create_client(); + if (client_data == NULL) { + PRINTF(("svcauth_gssapi: create_client failed\n")); + LOG_MISCERR("internal error creating client record"); + ret = AUTH_FAILED; + goto error; + } + } else { + if (creds.client_handle.length == 0) { + PRINTF(("svcauth_gssapi: expected non-empty creds\n")); + LOG_MISCERR("protocol error in client credentials"); + ret = AUTH_FAILED; + goto error; + } + + PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n", + *((rpc_u_int32 *) creds.client_handle.value), + creds.client_handle.length)); + + client_data = get_client(&creds.client_handle); + if (client_data == NULL) { + PRINTF(("svcauth_gssapi: client_handle lookup failed\n")); + LOG_MISCERR("invalid client handle received"); + ret = AUTH_BADCRED; + goto error; + } + PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n")); + } + + /* any response we send will use client_handle, so set it now */ + call_res.client_handle.length = sizeof(client_data->key); + call_res.client_handle.value = (char *) &client_data->key; + + /* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */ + rqst->rq_xprt->xp_auth = &client_data->svcauth; + + if (client_data->established == FALSE) { + PRINTF(("svcauth_gssapi: context is not established\n")); + + if (creds.auth_msg == FALSE) { + PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n")); + LOG_MISCERR("protocol error on incomplete connection"); + ret = AUTH_REJECTEDCRED; + goto error; + } + + /* + * If the context is not established, then only GSSAPI_INIT + * and _CONTINUE requests are valid. + */ + if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc != + AUTH_GSSAPI_CONTINUE_INIT) { + PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", + rqst->rq_proc)); + LOG_MISCERR("protocol error on incomplete connection"); + ret = AUTH_FAILED; + goto error; + } + + /* call is for us, deserialize arguments */ + memset(&call_arg, 0, sizeof(call_arg)); + if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg, + &call_arg)) { + PRINTF(("svcauth_gssapi: cannot decode args\n")); + LOG_MISCERR("protocol error in procedure arguments"); + ret = AUTH_BADCRED; + goto error; + } + + /* + * Process the call arg version number. + * + * Set the krb5_gss backwards-compatibility mode based on client + * version. This controls whether the AP_REP message is + * encrypted with the session key (version 2+, correct) or the + * session subkey (version 1, incorrect). This function can + * never fail, so we don't bother checking its return value. + */ + switch (call_arg.version) { + case 1: + case 2: + LOG_MISCERR("Warning: Accepted old RPC protocol request"); + call_res.version = 1; + break; + case 3: + call_res.version = call_arg.version; + break; + default: + PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n")); + LOG_MISCERR("unsupported GSSAPI_INIT version"); + ret = AUTH_BADCRED; + goto error; + } + +#ifdef GSS_BACKWARD_HACK + krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1); +#endif + + if (call_arg.version == 3) { + int len; + + memset(&bindings, 0, sizeof(bindings)); + bindings.application_data.length = 0; + bindings.initiator_addrtype = GSS_C_AF_INET; + bindings.initiator_address.length = 4; + bindings.initiator_address.value = + &svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr; + + len = sizeof(sockname); + if (getsockname(rqst->rq_xprt->xp_sock, + (struct sockaddr *) &sockname, &len) < 0) { + LOG_MISCERR("cannot get local address"); + PRINTF(("svcauth_gssapi: errno %d while getting address", + errno)); + ret = AUTH_FAILED; + goto error; + } + + bindings.acceptor_addrtype = GSS_C_AF_INET; + bindings.acceptor_address.length = 4; + bindings.acceptor_address.value = &sockname.sin_addr.s_addr; + + bindp = &bindings; + } else { + bindp = GSS_C_NO_CHANNEL_BINDINGS; + } + + /* + * If the client's server_creds is already set, use it. + * Otherwise, try each credential in server_creds_list until + * one of them succeedes, then set the client server_creds + * to that. If all fail, the client's server_creds isn't + * set (which is fine, because the client will be gc'ed + * anyway). + * + * If accept_sec_context returns something other than + * success and GSS_S_FAILURE, then assume different + * credentials won't help and stop looping. + * + * Note that there are really two cases here: (1) the client + * has a server_creds already, and (2) it does not. They + * are both written in the same loop so that there is only + * one textual call to gss_accept_sec_context; in fact, in + * case (1), the loop is executed exactly once. + */ + for (i = 0; i < server_creds_count; i++) { + if (client_data->server_creds != NULL) { + PRINTF(("svcauth_gssapi: using's clients server_creds\n")); + server_creds = client_data->server_creds; + } else { + PRINTF(("svcauth_gssapi: trying creds %d\n", i)); + server_creds = server_creds_list[i]; + } + + call_res.gss_major = + gss_accept_sec_context(&call_res.gss_minor, + &client_data->context, + server_creds, + &call_arg.token, + bindp, + &client_data->client_name, + NULL, + &output_token, + &ret_flags, + &time_rec, + NULL); + + if (server_creds == client_data->server_creds) + break; + + if (call_res.gss_major == GSS_S_COMPLETE || + call_res.gss_major == GSS_S_CONTINUE_NEEDED) { + /* server_creds was right, set it! */ + PRINTF(("svcauth_gssapi: creds are correct, storing\n")); + client_data->server_creds = server_creds; + client_data->server_name = server_name_list[i]; + break; + } else if (call_res.gss_major != GSS_S_FAILURE || + /* + * XXX hard-coded because there is no other + * way to prevent all GSS_S_FAILURES from + * returning a "wrong principal in request" + * error + */ + ((krb5_error_code) call_res.gss_minor != + (krb5_error_code) KRB5KRB_AP_WRONG_PRINC)) { + break; + } + } + + gssstat = call_res.gss_major; + minor_stat = call_res.gss_minor; + + /* done with call args */ + xdr_free(xdr_authgssapi_init_arg, &call_arg); + + PRINTF(("svcauth_gssapi: accept_sec_context returned %#x\n", + call_res.gss_major)); + if (call_res.gss_major != GSS_S_COMPLETE && + call_res.gss_major != GSS_S_CONTINUE_NEEDED) { + AUTH_GSSAPI_DISPLAY_STATUS(("accepting context", + call_res.gss_major, + call_res.gss_minor)); + + if (log_badauth != NULL) + (*log_badauth)(call_res.gss_major, + call_res.gss_minor, + &rqst->rq_xprt->xp_raddr, + log_badauth_data); + + svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res, + (caddr_t) &call_res); + *no_dispatch = TRUE; + ret = AUTH_OK; + goto error; + } + + if (output_token.length != 0) { + PRINTF(("svcauth_gssapi: got new output token\n")); + GSS_COPY_BUFFER(call_res.token, output_token); + } + + if (gssstat == GSS_S_COMPLETE) { + client_data->seq_num = rand(); + client_expire(client_data, + (time_rec == GSS_C_INDEFINITE ? + INDEF_EXPIRE : time_rec) + time(0)); + + PRINTF(("svcauth_gssapi: context established, isn %d\n", + client_data->seq_num)); + + if (auth_gssapi_seal_seq(client_data->context, + client_data->seq_num, + &call_res.signed_isn) == + FALSE) { + ret = AUTH_FAILED; + LOG_MISCERR("internal error sealing sequence number"); + goto error; + } + } + + PRINTF(("svcauth_gssapi: sending reply\n")); + svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res, + (caddr_t) &call_res); + *no_dispatch = TRUE; + + /* + * If appropriate, set established to TRUE *after* sending + * response (otherwise, the client will receive the final + * token encrypted) + */ + if (gssstat == GSS_S_COMPLETE) { + gss_release_buffer(&minor_stat, &call_res.signed_isn); + client_data->established = TRUE; + } + gss_release_buffer(&minor_stat, &output_token); + } else { + PRINTF(("svcauth_gssapi: context is established\n")); + + /* check the verifier */ + PRINTF(("svcauth_gssapi: checking verifier, len %d\n", + verf->oa_length)); + + in_buf.length = verf->oa_length; + in_buf.value = verf->oa_base; + + if (auth_gssapi_unseal_seq(client_data->context, &in_buf, + &seq_num) == FALSE) { + ret = AUTH_BADVERF; + LOG_MISCERR("internal error unsealing sequence number"); + goto error; + } + + if (seq_num != client_data->seq_num + 1) { + PRINTF(("svcauth_gssapi: expected isn %d, got %d\n", + client_data->seq_num + 1, seq_num)); + if (log_badverf != NULL) + (*log_badverf)(client_data->client_name, + client_data->server_name, + rqst, msg, log_badverf_data); + + ret = AUTH_REJECTEDVERF; + goto error; + } + client_data->seq_num++; + + PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num)); + + /* free previous response verifier, if any */ + if (client_data->prev_verf.length != 0) { + gss_release_buffer(&minor_stat, &client_data->prev_verf); + client_data->prev_verf.length = 0; + } + + /* prepare response verifier */ + seq_num = client_data->seq_num + 1; + if (auth_gssapi_seal_seq(client_data->context, seq_num, + &out_buf) == FALSE) { + ret = AUTH_FAILED; + LOG_MISCERR("internal error sealing sequence number"); + goto error; + } + + client_data->seq_num++; + + PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num)); + + rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI; + rqst->rq_xprt->xp_verf.oa_base = out_buf.value; + rqst->rq_xprt->xp_verf.oa_length = out_buf.length; + + /* save verifier so it can be freed next time */ + client_data->prev_verf.value = out_buf.value; + client_data->prev_verf.length = out_buf.length; + + /* + * Message is authentic. If auth_msg if true, process the + * call; otherwise, return AUTH_OK so it will be dispatched + * to the application server. + */ + + if (creds.auth_msg == TRUE) { + /* + * If process_token fails, then the token probably came + * from an attacker. No response (error or otherwise) + * should be returned to the client, since it won't be + * accepting one. + */ + + switch (rqst->rq_proc) { + case AUTH_GSSAPI_MSG: + PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n")); + memset(&call_arg, 0, sizeof(call_arg)); + if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg, + &call_arg)) { + PRINTF(("svcauth_gssapi: cannot decode args\n")); + LOG_MISCERR("protocol error in call arguments"); + ret = AUTH_BADCRED; + goto error; + } + + PRINTF(("svcauth_gssapi: processing token\n")); + gssstat = gss_process_context_token(&minor_stat, + client_data->context, + &call_arg.token); + + /* done with call args */ + xdr_free(xdr_authgssapi_init_arg, &call_arg); + + if (gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("processing token", + gssstat, minor_stat)); + ret = AUTH_FAILED; + goto error; + } + + svc_sendreply(rqst->rq_xprt, xdr_void, NULL); + *no_dispatch = TRUE; + break; + + case AUTH_GSSAPI_DESTROY: + PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n")); + + PRINTF(("svcauth_gssapi: sending reply\n")); + svc_sendreply(rqst->rq_xprt, xdr_void, NULL); + *no_dispatch = TRUE; + + destroy_client(client_data); + break; + + default: + PRINTF(("svcauth_gssapi: unacceptable procedure %d\n", + rqst->rq_proc)); + LOG_MISCERR("invalid call procedure number"); + ret = AUTH_FAILED; + goto error; + } + } else { + /* set credentials for app server; comment in svc.c */ + /* seems to imply this is incorrect, but I don't see */ + /* any problem with it... */ + rqst->rq_clntcred = (char *)client_data->client_name; + rqst->rq_svccred = (char *)client_data->context; + } + } + + if (creds.client_handle.length != 0) { + PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", + creds.client_handle.length)); + xdr_free(xdr_authgssapi_creds, &creds); + } + + PRINTF(("\n")); + return AUTH_OK; + +error: + if (creds.client_handle.length != 0) { + PRINTF(("svcauth_gssapi: freeing client_handle len %d\n", + creds.client_handle.length)); + xdr_free(xdr_authgssapi_creds, &creds); + } + + PRINTF(("\n")); + return ret; +} + +static void cleanup() +{ + client_list *c, *c2; + + PRINTF(("cleanup_and_exit: starting\n")); + + c = clients; + while (c) { + c2 = c; + c = c->next; + destroy_client(c2->client); + free(c2); + } + + exit(0); +} + +/* + * Function: create_client + * + * Purpose: Creates an new client_data structure and stores it in the + * database. + * + * Returns: the new client_data structure, or NULL on failure. + * + * Effects: + * + * A new client_data is created and stored in the hash table and + * b-tree. A new key that is unique in the current database is + * chosen; this key should be used as the client's client_handle. + */ +static svc_auth_gssapi_data *create_client() +{ + client_list *c; + svc_auth_gssapi_data *client_data; + static int client_key = 1; + int ret; + + PRINTF(("svcauth_gssapi: empty creds, creating\n")); + + client_data = (svc_auth_gssapi_data *) malloc(sizeof(*client_data)); + if (client_data == NULL) + return NULL; + memset((char *) client_data, 0, sizeof(*client_data)); + L_PRINTF(2, ("create_client: new client_data = %#x\n", client_data)); + + /* set up client data structure */ + client_data->established = 0; + client_data->context = GSS_C_NO_CONTEXT; + client_data->expiration = time(0) + INITIATION_TIMEOUT; + + /* set up psycho-recursive SVCAUTH hack */ + client_data->svcauth.svc_ah_ops = &svc_auth_gssapi_ops; + client_data->svcauth.svc_ah_private = (caddr_t) client_data; + + client_data->key = client_key++; + + c = (client_list *) malloc(sizeof(client_list)); + if (c == NULL) + return NULL; + c->client = client_data; + c->next = NULL; + + + if (clients == NULL) + clients = c; + else { + c->next = clients; + clients = c; + } + + PRINTF(("svcauth_gssapi: new handle %d\n", client_data->key)); + L_PRINTF(2, ("create_client: done\n")); + + return client_data; +} + +/* + * Function: client_expire + * + * Purpose: change the expiration time of a client in the database + * + * Arguments: + * + * client_data (r) the client_data to expire + * exp (r) the new expiration time + * + * Effects: + * + * client_data->expiration = exp + * + * This function used to remove client_data from the database, change + * its expiration time, and re-add it, which was necessary because the + * database was sorted by expiration time so a simple modification + * would break the rep invariant. Now the database is an unsorted + * linked list, so it doesn't matter. + */ +static void client_expire(svc_auth_gssapi_data *client_data, rpc_u_int32 exp) +{ + client_data->expiration = exp; +} + +/* + * Function get_client + * + * Purpose: retrieve a client_data structure from the database based + * on its client handle (key) + * + * Arguments: + * + * client_handle (r) the handle (key) to retrieve + * + * Effects: + * + * Searches the list and returns the client_data whose key field + * matches the contents of client_handle, or returns NULL if none was + * found. + */ +static svc_auth_gssapi_data *get_client(gss_buffer_t client_handle) +{ + client_list *c; + rpc_u_int32 handle; + + memcpy(&handle, client_handle->value, 4); + + L_PRINTF(2, ("get_client: looking for client %d\n", handle)); + + c = clients; + while (c) { + if (c->client->key == handle) + return c->client; + c = c->next; + } + + L_PRINTF(2, ("get_client: client_handle lookup failed\n")); + return NULL; +} + +/* + * Function: destroy_client + * + * Purpose: destroys a client entry and removes it from the database + * + * Arguments: + * + * client_data (r) the client to be destroyed + * + * Effects: + * + * client_data->context is deleted with gss_delete_sec_context. + * client_data's entry in the database is destroyed. client_data is + * freed. + */ +static void destroy_client(svc_auth_gssapi_data *client_data) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc out_buf; + client_list *c, *c2; + int ret; + + PRINTF(("destroy_client: destroying client_data\n")); + L_PRINTF(2, ("destroy_client: client_data = %#x\n", client_data)); + +#ifdef DEBUG_GSSAPI + if (svc_debug_gssapi >= 3) + dump_db("before frees"); +#endif + + /* destroy client struct even if error occurs */ + + gssstat = gss_delete_sec_context(&minor_stat, &client_data->context, + &out_buf); + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat, + minor_stat)); + + gss_release_buffer(&minor_stat, &out_buf); + gss_release_name(&minor_stat, &client_data->client_name); + if (client_data->prev_verf.length != 0) + gss_release_buffer(&minor_stat, &client_data->prev_verf); + + if (clients == NULL) { + PRINTF(("destroy_client: called on empty database\n")); + abort(); + } else if (clients->client == client_data) { + c = clients; + clients = clients->next; + free(c); + } else { + c2 = clients; + c = clients->next; + while (c) { + if (c->client == client_data) { + c2->next = c->next; + free(c); + goto done; + } else + c = c->next; + } + PRINTF(("destroy_client: client_handle delete failed\n")); + abort(); + } + +done: + + L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key)); + + free(client_data); + +#ifdef PURIFY + purify_watch_n(client_data, sizeof(*client_data), "rw"); +#endif +} + +static void dump_db(char *msg) +{ + svc_auth_gssapi_data *client_data; + client_list *c; + + L_PRINTF(3, ("dump_db: %s:\n", msg)); + + c = clients; + while (c) { + client_data = c->client; + L_PRINTF(3, ("\tclient_data = %#x, exp = %d\n", + client_data, client_data->expiration)); + c = c->next; + } + + L_PRINTF(3, ("\n")); +} + +static void clean_client() +{ + svc_auth_gssapi_data *client_data; + client_list *c; + + PRINTF(("clean_client: starting\n")); + + c = clients; + while (c) { + client_data = c->client; + + L_PRINTF(2, ("clean_client: client_data = %#x\n", + client_data)); + + if (client_data->expiration < time(0)) { + PRINTF(("clean_client: client %d expired\n", + client_data->key)); + destroy_client(client_data); + c = clients; /* start over, just to be safe */ + } else { + c = c->next; + } + } + +done: + PRINTF(("clean_client: done\n")); +} + +/* + * Function: _svcauth_gssapi_set_name + * + * Purpose: Sets the list of service names for which incoming + * authentication requests should be honored. + * + * See functional specifications. + */ +bool_t _svcauth_gssapi_set_names(auth_gssapi_name *names, int num) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc in_buf; + int i; + + if (num == 0) + for (; names[num].name != NULL; num++) + ; + + server_creds_list = NULL; + server_name_list = NULL; + + server_creds_list = (gss_cred_id_t *) malloc(num*sizeof(gss_cred_id_t)); + if (server_creds_list == NULL) + goto fail; + server_name_list = (gss_name_t *) malloc(num*sizeof(gss_name_t)); + if (server_name_list == NULL) + goto fail; + + for (i = 0; i < num; i++) { + in_buf.value = names[i].name; + in_buf.length = strlen(in_buf.value) + 1; + + gssstat = gss_import_name(&minor_stat, &in_buf, names[i].type, + &server_name_list[i]); + + if (gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("importing name", gssstat, + minor_stat)); + goto fail; + } + + gssstat = gss_acquire_cred(&minor_stat, server_name_list[i], 0, + GSS_C_NULL_OID_SET, GSS_C_ACCEPT, + &server_creds_list[i], NULL, NULL); + if (gssstat != GSS_S_COMPLETE) { + AUTH_GSSAPI_DISPLAY_STATUS(("acquiring credentials", + gssstat, minor_stat)); + goto fail; + } + } + + server_creds_count = num; + + return TRUE; + +fail: + /* memory leak: not releasing names/creds already acquired */ + if (server_creds_list) + free(server_creds_list); + if (server_name_list) + free(server_name_list); + return FALSE; +} + +/* + * Function: _svcauth_gssapi_set_log_badauth_func + * + * Purpose: sets the logging function called when an invalid RPC call + * arrives + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_badauth_func + (auth_gssapi_log_badauth_func func, caddr_t data) +{ + log_badauth = func; + log_badauth_data = data; +} + +/* + * Function: _svcauth_gssapi_set_log_badverf_func + * + * Purpose: sets the logging function called when an invalid RPC call + * arrives + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_badverf_func + (auth_gssapi_log_badverf_func func, caddr_t data) +{ + log_badverf = func; + log_badverf_data = data; +} + +/* + * Function: _svcauth_gssapi_set_log_miscerr_func + * + * Purpose: sets the logging function called when a miscellaneous + * AUTH_GSSAPI error occurs + * + * See functional specifications. + */ +void _svcauth_gssapi_set_log_miscerr_func + (auth_gssapi_log_miscerr_func func, caddr_t data) +{ + log_miscerr = func; + log_miscerr_data = data; +} + +/* + * Encrypt the serialized arguments from xdr_func applied to xdr_ptr + * and write the result to xdrs. + */ +static bool_t svc_auth_gssapi_wrap(auth, out_xdrs, xdr_func, xdr_ptr) + SVCAUTH *auth; + XDR *out_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + OM_uint32 gssstat, minor_stat; + + if (! SVCAUTH_PRIVATE(auth)->established) { + PRINTF(("svc_gssapi_wrap: not established, noop\n")); + return (*xdr_func)(out_xdrs, xdr_ptr); + } else if (! auth_gssapi_wrap_data(&gssstat, &minor_stat, + SVCAUTH_PRIVATE(auth)->context, + SVCAUTH_PRIVATE(auth)->seq_num, + out_xdrs, xdr_func, xdr_ptr)) { + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("encrypting function arguments", + gssstat, minor_stat)); + return FALSE; + } else + return TRUE; +} + +static bool_t svc_auth_gssapi_unwrap(auth, in_xdrs, xdr_func, xdr_ptr) + SVCAUTH *auth; + XDR *in_xdrs; + bool_t (*xdr_func)(); + caddr_t xdr_ptr; +{ + svc_auth_gssapi_data *client_data = SVCAUTH_PRIVATE(auth); + OM_uint32 gssstat, minor_stat; + + if (! client_data->established) { + PRINTF(("svc_gssapi_unwrap: not established, noop\n")); + return (*xdr_func)(in_xdrs, (auth_gssapi_init_arg *) xdr_ptr); + } else if (! auth_gssapi_unwrap_data(&gssstat, &minor_stat, + client_data->context, + client_data->seq_num-1, + in_xdrs, xdr_func, xdr_ptr)) { + if (gssstat != GSS_S_COMPLETE) + AUTH_GSSAPI_DISPLAY_STATUS(("decrypting function arguments", + gssstat, minor_stat)); + return FALSE; + } else + return TRUE; +} diff --git a/src/lib/rpc/svc_auth_unix.c b/src/lib/rpc/svc_auth_unix.c new file mode 100644 index 000000000..1ff21588c --- /dev/null +++ b/src/lib/rpc/svc_auth_unix.c @@ -0,0 +1,137 @@ +/* @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC; from 1.28 88/02/08 SMI */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_auth_unix.c + * Handles UNIX flavor authentication parameters on the service side of rpc. + * There are two svc auth implementations here: AUTH_UNIX and AUTH_SHORT. + * _svcauth_unix does full blown unix style uid,gid+gids auth, + * _svcauth_short uses a shorthand auth to index into a cache of longhand auths. + * Note: the shorthand has been gutted for efficiency. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include + +/* + * Unix longhand authenticator + */ +enum auth_stat +_svcauth_unix(rqst, msg) + register struct svc_req *rqst; + register struct rpc_msg *msg; +{ + register enum auth_stat stat; + XDR xdrs; + register struct authunix_parms *aup; + register rpc_int32 *buf; + struct area { + struct authunix_parms area_aup; + char area_machname[MAX_MACHINE_NAME+1]; + int area_gids[NGRPS]; + } *area; + unsigned int auth_len; + int str_len, gid_len; + register int i; + + rqst->rq_xprt->xp_auth = &svc_auth_any; + + area = (struct area *) rqst->rq_clntcred; + aup = &area->area_aup; + aup->aup_machname = area->area_machname; + aup->aup_gids = area->area_gids; + auth_len = (unsigned int)msg->rm_call.cb_cred.oa_length; + xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len,XDR_DECODE); + buf = (rpc_int32 *) XDR_INLINE(&xdrs, auth_len); + if (buf != NULL) { + aup->aup_time = IXDR_GET_LONG(buf); + str_len = IXDR_GET_U_LONG(buf); + if (str_len > MAX_MACHINE_NAME) { + stat = AUTH_BADCRED; + goto done; + } + memmove(aup->aup_machname, (caddr_t)buf, (unsigned int)str_len); + aup->aup_machname[str_len] = 0; + str_len = RNDUP(str_len); + buf += str_len / sizeof (rpc_int32); + aup->aup_uid = IXDR_GET_LONG(buf); + aup->aup_gid = IXDR_GET_LONG(buf); + gid_len = IXDR_GET_U_LONG(buf); + if (gid_len > NGRPS) { + stat = AUTH_BADCRED; + goto done; + } + aup->aup_len = gid_len; + for (i = 0; i < gid_len; i++) { + aup->aup_gids[i] = IXDR_GET_LONG(buf); + } + /* + * five is the smallest unix credentials structure - + * timestamp, hostname len (0), uid, gid, and gids len (0). + */ + if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) { + (void) printf("bad auth_len gid %d str %d auth %d\n", + gid_len, str_len, auth_len); + stat = AUTH_BADCRED; + goto done; + } + } else if (! xdr_authunix_parms(&xdrs, aup)) { + xdrs.x_op = XDR_FREE; + (void)xdr_authunix_parms(&xdrs, aup); + stat = AUTH_BADCRED; + goto done; + } + rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; + rqst->rq_xprt->xp_verf.oa_length = 0; + stat = AUTH_OK; +done: + XDR_DESTROY(&xdrs); + return (stat); +} + + +/* + * Shorthand unix authenticator + * Looks up longhand in a cache. + */ +/*ARGSUSED*/ +enum auth_stat +_svcauth_short(rqst, msg) + struct svc_req *rqst; + struct rpc_msg *msg; +{ + rqst->rq_xprt->xp_auth = &svc_auth_any; + return (AUTH_REJECTEDCRED); +} diff --git a/src/lib/rpc/svc_raw.c b/src/lib/rpc/svc_raw.c new file mode 100644 index 000000000..1170ecec8 --- /dev/null +++ b/src/lib/rpc/svc_raw.c @@ -0,0 +1,166 @@ +/* @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_raw.c, This a toy for simple testing and timing. + * Interface to create an rpc client and server in the same UNIX process. + * This lets us similate rpc and get rpc (round trip) overhead, without + * any interference from the kernal. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include + + +/* + * This is the "network" that we will be moving data over + */ +static struct svcraw_private { + char _raw_buf[UDPMSGSIZE]; + SVCXPRT server; + XDR xdr_stream; + char verf_body[MAX_AUTH_BYTES]; +} *svcraw_private; + +static bool_t svcraw_recv(); +static enum xprt_stat svcraw_stat(); +static bool_t svcraw_getargs(); +static bool_t svcraw_reply(); +static bool_t svcraw_freeargs(); +static void svcraw_destroy(); + +static struct xp_ops server_ops = { + svcraw_recv, + svcraw_stat, + svcraw_getargs, + svcraw_reply, + svcraw_freeargs, + svcraw_destroy +}; + +SVCXPRT * +svcraw_create() +{ + register struct svcraw_private *srp = svcraw_private; + + if (srp == 0) { + srp = (struct svcraw_private *)calloc(1, sizeof (*srp)); + if (srp == 0) + return (0); + } + srp->server.xp_sock = 0; + srp->server.xp_port = 0; + srp->server.xp_ops = &server_ops; + srp->server.xp_verf.oa_base = srp->verf_body; + xdrmem_create(&srp->xdr_stream, srp->_raw_buf, UDPMSGSIZE, XDR_FREE); + return (&srp->server); +} + +static enum xprt_stat +svcraw_stat() +{ + + return (XPRT_IDLE); +} + +static bool_t +svcraw_recv(xprt, msg) + SVCXPRT *xprt; + struct rpc_msg *msg; +{ + register struct svcraw_private *srp = svcraw_private; + register XDR *xdrs; + + if (srp == 0) + return (0); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_DECODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_callmsg(xdrs, msg)) + return (FALSE); + return (TRUE); +} + +static bool_t +svcraw_reply(xprt, msg) + SVCXPRT *xprt; + struct rpc_msg *msg; +{ + register struct svcraw_private *srp = svcraw_private; + register XDR *xdrs; + + if (srp == 0) + return (FALSE); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_replymsg(xdrs, msg)) + return (FALSE); + (void)XDR_GETPOS(xdrs); /* called just for overhead */ + return (TRUE); +} + +static bool_t +svcraw_getargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + register struct svcraw_private *srp = svcraw_private; + + if (srp == 0) + return (FALSE); + return ((*xdr_args)(&srp->xdr_stream, args_ptr)); +} + +static bool_t +svcraw_freeargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + register struct svcraw_private *srp = svcraw_private; + register XDR *xdrs; + + if (srp == 0) + return (FALSE); + xdrs = &srp->xdr_stream; + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +static void +svcraw_destroy() +{ +} diff --git a/src/lib/rpc/svc_run.c b/src/lib/rpc/svc_run.c new file mode 100644 index 000000000..d43aa2425 --- /dev/null +++ b/src/lib/rpc/svc_run.c @@ -0,0 +1,72 @@ +/* @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro"; +#endif + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * This is the rpc server side idle loop + * Wait for input, call server program. + */ +#include +#include + +void +svc_run() +{ +#ifdef FD_SETSIZE + fd_set readfds; +#else + int readfds; +#endif /* def FD_SETSIZE */ + extern int errno; + + for (;;) { +#ifdef FD_SETSIZE + readfds = svc_fdset; +#else + readfds = svc_fds; +#endif /* def FD_SETSIZE */ + switch (select(_rpc_dtablesize(), &readfds, (fd_set *)0, + (fd_set *)0, (struct timeval *)0)) { + case -1: + if (errno == EINTR) { + continue; + } + perror("svc_run: - select failed"); + return; + case 0: + continue; + default: + svc_getreqset(&readfds); + } + } +} diff --git a/src/lib/rpc/svc_simple.c b/src/lib/rpc/svc_simple.c new file mode 100644 index 000000000..2a22e5722 --- /dev/null +++ b/src/lib/rpc/svc_simple.c @@ -0,0 +1,143 @@ +/* @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_simple.c + * Simplified front end to rpc. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include + +static struct proglst { + char *(*p_progname)(); + int p_prognum; + int p_procnum; + xdrproc_t p_inproc, p_outproc; + struct proglst *p_nxt; +} *proglst; +static void universal(); +static SVCXPRT *transp; +struct proglst *pl; + +registerrpc(prognum, versnum, procnum, progname, inproc, outproc) + char *(*progname)(); + xdrproc_t inproc, outproc; +{ + + if (procnum == NULLPROC) { + (void) fprintf(stderr, + "can't reassign procedure number %d\n", NULLPROC); + return (-1); + } + if (transp == 0) { + transp = svcudp_create(RPC_ANYSOCK); + if (transp == NULL) { + (void) fprintf(stderr, "couldn't create an rpc server\n"); + return (-1); + } + } + (void) pmap_unset((rpc_u_int32)prognum, (rpc_u_int32)versnum); + if (!svc_register(transp, (rpc_u_int32)prognum, (rpc_u_int32)versnum, + universal, IPPROTO_UDP)) { + (void) fprintf(stderr, "couldn't register prog %d vers %d\n", + prognum, versnum); + return (-1); + } + pl = (struct proglst *)malloc(sizeof(struct proglst)); + if (pl == NULL) { + (void) fprintf(stderr, "registerrpc: out of memory\n"); + return (-1); + } + pl->p_progname = progname; + pl->p_prognum = prognum; + pl->p_procnum = procnum; + pl->p_inproc = inproc; + pl->p_outproc = outproc; + pl->p_nxt = proglst; + proglst = pl; + return (0); +} + +static void +universal(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + int prog, proc; + char *outdata; + char xdrbuf[UDPMSGSIZE]; + struct proglst *pl; + + /* + * enforce "procnum 0 is echo" convention + */ + if (rqstp->rq_proc == NULLPROC) { + if (svc_sendreply(transp, xdr_void, (char *)NULL) == FALSE) { + (void) fprintf(stderr, "xxx\n"); + exit(1); + } + return; + } + prog = rqstp->rq_prog; + proc = rqstp->rq_proc; + for (pl = proglst; pl != NULL; pl = pl->p_nxt) + if (pl->p_prognum == prog && pl->p_procnum == proc) { + /* decode arguments into a CLEAN buffer */ + memset(xdrbuf, 0, sizeof(xdrbuf)); /* required ! */ + if (!svc_getargs(transp, pl->p_inproc, xdrbuf)) { + svcerr_decode(transp); + return; + } + outdata = (*(pl->p_progname))(xdrbuf); + if (outdata == NULL && pl->p_outproc != xdr_void) + /* there was an error */ + return; + if (!svc_sendreply(transp, pl->p_outproc, outdata)) { + (void) fprintf(stderr, + "trouble replying to prog %d\n", + pl->p_prognum); + exit(1); + } + /* free the decoded arguments */ + (void)svc_freeargs(transp, pl->p_inproc, xdrbuf); + return; + } + (void) fprintf(stderr, "never registered prog %d\n", prog); + exit(1); +} + diff --git a/src/lib/rpc/svc_tcp.c b/src/lib/rpc/svc_tcp.c new file mode 100644 index 000000000..e20a29b19 --- /dev/null +++ b/src/lib/rpc/svc_tcp.c @@ -0,0 +1,453 @@ +/* @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_tcp.c, Server side for TCP/IP based RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * Actually implements two flavors of transporter - + * a tcp rendezvouser (a listner and connection establisher) + * and a record/tcp stream. + */ + +#include +#include +#include +#include +#include +/*extern bool_t abort();*. +extern errno; + +/* + * Ops vector for TCP/IP based rpc service handle + */ +static bool_t svctcp_recv(); +static enum xprt_stat svctcp_stat(); +static bool_t svctcp_getargs(); +static bool_t svctcp_reply(); +static bool_t svctcp_freeargs(); +static void svctcp_destroy(); + +static struct xp_ops svctcp_op = { + svctcp_recv, + svctcp_stat, + svctcp_getargs, + svctcp_reply, + svctcp_freeargs, + svctcp_destroy +}; + +/* + * Ops vector for TCP/IP rendezvous handler + */ +static bool_t rendezvous_request(); +static bool_t abortx(); +static enum xprt_stat rendezvous_stat(); + +static struct xp_ops svctcp_rendezvous_op = { + rendezvous_request, + rendezvous_stat, + abortx, + abortx, + abortx, + svctcp_destroy +}; + +static int readtcp(), writetcp(); +static SVCXPRT *makefd_xprt(); + +struct tcp_rendezvous { /* kept in xprt->xp_p1 */ + unsigned int sendsize; + unsigned int recvsize; +}; + +struct tcp_conn { /* kept in xprt->xp_p1 */ + enum xprt_stat strm_stat; + rpc_u_int32 x_id; + XDR xdrs; + char verf_body[MAX_AUTH_BYTES]; +}; + +/* + * Usage: + * xprt = svctcp_create(sock, send_buf_size, recv_buf_size); + * + * Creates, registers, and returns a (rpc) tcp based transporter. + * Once *xprt is initialized, it is registered as a transporter + * see (svc.h, xprt_register). This routine returns + * a NULL if a problem occurred. + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svctcp_create + * binds it to an arbitrary port. The routine then starts a tcp + * listener on the socket's associated port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * + * Since tcp streams do buffered io similar to stdio, the caller can specify + * how big the send and receive buffers are via the second and third parms; + * 0 => use the system default. + */ +SVCXPRT * +svctcp_create(sock, sendsize, recvsize) + register int sock; + unsigned int sendsize; + unsigned int recvsize; +{ + bool_t madesock = FALSE; + register SVCXPRT *xprt; + register struct tcp_rendezvous *r; + struct sockaddr_in addr; + int len = sizeof(struct sockaddr_in); + + if (sock == RPC_ANYSOCK) { + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("svctcp_.c - udp socket creation problem"); + return ((SVCXPRT *)NULL); + } + madesock = TRUE; + } + memset((char *)&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport(sock, &addr)) { + addr.sin_port = 0; + (void)bind(sock, (struct sockaddr *)&addr, len); + } + if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { + perror("svc_tcp.c - cannot getsockname"); + if (madesock) + (void) close(sock); + return ((SVCXPRT *)NULL); + } + if (listen(sock, 2) != 0) { + perror("svctcp_.c - cannot listen"); + if (madesock) + (void)close(sock); + return ((SVCXPRT *)NULL); + } + r = (struct tcp_rendezvous *)mem_alloc(sizeof(*r)); + if (r == NULL) { + (void) fprintf(stderr, "svctcp_create: out of memory\n"); + return (NULL); + } + r->sendsize = sendsize; + r->recvsize = recvsize; + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == NULL) { + (void) fprintf(stderr, "svctcp_create: out of memory\n"); + return (NULL); + } + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t)r; + xprt->xp_verf = _null_auth; + xprt->xp_ops = &svctcp_rendezvous_op; + xprt->xp_port = ntohs(addr.sin_port); + xprt->xp_sock = sock; + xprt_register(xprt); + return (xprt); +} + +/* + * Like svtcp_create(), except the routine takes any *open* UNIX file + * descriptor as its first input. + */ +SVCXPRT * +svcfd_create(fd, sendsize, recvsize) + int fd; + unsigned int sendsize; + unsigned int recvsize; +{ + + return (makefd_xprt(fd, sendsize, recvsize)); +} + +static SVCXPRT * +makefd_xprt(fd, sendsize, recvsize) + int fd; + unsigned int sendsize; + unsigned int recvsize; +{ + register SVCXPRT *xprt; + register struct tcp_conn *cd; + + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == (SVCXPRT *)NULL) { + (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n"); + goto done; + } + cd = (struct tcp_conn *)mem_alloc(sizeof(struct tcp_conn)); + if (cd == (struct tcp_conn *)NULL) { + (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n"); + mem_free((char *) xprt, sizeof(SVCXPRT)); + xprt = (SVCXPRT *)NULL; + goto done; + } + cd->strm_stat = XPRT_IDLE; + xdrrec_create(&(cd->xdrs), sendsize, recvsize, + (caddr_t)xprt, readtcp, writetcp); + xprt->xp_p2 = NULL; + xprt->xp_p1 = (caddr_t)cd; + xprt->xp_verf.oa_base = cd->verf_body; + xprt->xp_addrlen = 0; + xprt->xp_ops = &svctcp_op; /* truely deals with calls */ + xprt->xp_port = 0; /* this is a connection, not a rendezvouser */ + xprt->xp_sock = fd; + xprt_register(xprt); + done: + return (xprt); +} + +static bool_t +rendezvous_request(xprt) + register SVCXPRT *xprt; +{ + int sock; + struct tcp_rendezvous *r; + struct sockaddr_in addr; + int len; + + r = (struct tcp_rendezvous *)xprt->xp_p1; + again: + len = sizeof(struct sockaddr_in); + if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr, + &len)) < 0) { + if (errno == EINTR) + goto again; + return (FALSE); + } + /* + * make a new transporter (re-uses xprt) + */ + xprt = makefd_xprt(sock, r->sendsize, r->recvsize); + xprt->xp_raddr = addr; + xprt->xp_addrlen = len; + return (FALSE); /* there is never an rpc msg to be processed */ +} + +static enum xprt_stat +rendezvous_stat() +{ + + return (XPRT_IDLE); +} + +static void +svctcp_destroy(xprt) + register SVCXPRT *xprt; +{ + register struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1; + + xprt_unregister(xprt); + (void)close(xprt->xp_sock); + if (xprt->xp_port != 0) { + /* a rendezvouser socket */ + xprt->xp_port = 0; + } else { + /* an actual connection socket */ + XDR_DESTROY(&(cd->xdrs)); + } + mem_free((caddr_t)cd, sizeof(struct tcp_conn)); + mem_free((caddr_t)xprt, sizeof(SVCXPRT)); +} + +/* + * All read operations timeout after 35 seconds. + * A timeout is fatal for the connection. + */ +static struct timeval wait_per_try = { 35, 0 }; + +/* + * reads data from the tcp conection. + * any error is fatal and the connection is closed. + * (And a read of zero bytes is a half closed stream => error.) + */ +static int +readtcp(xprt, buf, len) + register SVCXPRT *xprt; + caddr_t buf; + register int len; +{ + register int sock = xprt->xp_sock; +#ifdef FD_SETSIZE + fd_set mask; + fd_set readfds; + + FD_ZERO(&mask); + FD_SET(sock, &mask); +#else + register int mask = 1 << sock; + int readfds; +#endif /* def FD_SETSIZE */ + do { + readfds = mask; + if (select(_rpc_dtablesize(), &readfds, (fd_set*)NULL, + (fd_set*)NULL, &wait_per_try) <= 0) { + if (errno == EINTR) { + continue; + } + goto fatal_err; + } +#ifdef FD_SETSIZE + } while (!FD_ISSET(sock, &readfds)); +#else + } while (readfds != mask); +#endif /* def FD_SETSIZE */ + if ((len = read(sock, buf, len)) > 0) { + return (len); + } +fatal_err: + ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED; + return (-1); +} + +/* + * writes data to the tcp connection. + * Any error is fatal and the connection is closed. + */ +static int +writetcp(xprt, buf, len) + register SVCXPRT *xprt; + caddr_t buf; + int len; +{ + register int i, cnt; + + for (cnt = len; cnt > 0; cnt -= i, buf += i) { + if ((i = write(xprt->xp_sock, buf, cnt)) < 0) { + ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = + XPRT_DIED; + return (-1); + } + } + return (len); +} + +static enum xprt_stat +svctcp_stat(xprt) + SVCXPRT *xprt; +{ + register struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + + if (cd->strm_stat == XPRT_DIED) + return (XPRT_DIED); + if (! xdrrec_eof(&(cd->xdrs))) + return (XPRT_MOREREQS); + return (XPRT_IDLE); +} + +static bool_t +svctcp_recv(xprt, msg) + SVCXPRT *xprt; + register struct rpc_msg *msg; +{ + register struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + register XDR *xdrs = &(cd->xdrs); + + xdrs->x_op = XDR_DECODE; + (void)xdrrec_skiprecord(xdrs); + if (xdr_callmsg(xdrs, msg)) { + cd->x_id = msg->rm_xid; + return (TRUE); + } + return (FALSE); +} + +static bool_t +svctcp_getargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + return (SVCAUTH_UNWRAP(xprt->xp_auth, + &(((struct tcp_conn *)(xprt->xp_p1))->xdrs), + xdr_args, args_ptr)); +} + +static bool_t +svctcp_freeargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + register XDR *xdrs = + &(((struct tcp_conn *)(xprt->xp_p1))->xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +static bool_t svctcp_reply(xprt, msg) + SVCXPRT *xprt; + register struct rpc_msg *msg; +{ + register struct tcp_conn *cd = + (struct tcp_conn *)(xprt->xp_p1); + register XDR *xdrs = &(cd->xdrs); + register bool_t stat; + + xdrproc_t xdr_results; + caddr_t xdr_location; + bool_t has_args; + + if (msg->rm_reply.rp_stat == MSG_ACCEPTED && + msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { + has_args = TRUE; + xdr_results = msg->acpted_rply.ar_results.proc; + xdr_location = msg->acpted_rply.ar_results.where; + + msg->acpted_rply.ar_results.proc = xdr_void; + msg->acpted_rply.ar_results.where = NULL; + } else + has_args = FALSE; + + xdrs->x_op = XDR_ENCODE; + msg->rm_xid = cd->x_id; + stat = FALSE; + if (xdr_replymsg(xdrs, msg) && + (!has_args || + (SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) { + stat = TRUE; + } + (void)xdrrec_endofrecord(xdrs, TRUE); + return (stat); +} + +static bool_t abortx() +{ + abort(); + return 1; +} + diff --git a/src/lib/rpc/svc_udp.c b/src/lib/rpc/svc_udp.c new file mode 100644 index 000000000..d44c7f5d8 --- /dev/null +++ b/src/lib/rpc/svc_udp.c @@ -0,0 +1,496 @@ +/* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * svc_udp.c, + * Server side for UDP/IP based RPC. (Does some caching in the hopes of + * achieving execute-at-most-once semantics.) + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#include +#include +#include +#include + + +#define rpc_buffer(xprt) ((xprt)->xp_p1) +#ifndef MAX +#define MAX(a, b) ((a > b) ? a : b) +#endif + +static bool_t svcudp_recv(); +static bool_t svcudp_reply(); +static enum xprt_stat svcudp_stat(); +static bool_t svcudp_getargs(); +static bool_t svcudp_freeargs(); +static void svcudp_destroy(); + +static void cache_set(); +static int cache_get(); + +static struct xp_ops svcudp_op = { + svcudp_recv, + svcudp_stat, + svcudp_getargs, + svcudp_reply, + svcudp_freeargs, + svcudp_destroy +}; + +extern int errno; + +/* + * kept in xprt->xp_p2 + */ +struct svcudp_data { + unsigned int su_iosz; /* byte size of send.recv buffer */ + rpc_u_int32 su_xid; /* transaction id */ + XDR su_xdrs; /* XDR handle */ + char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ + char * su_cache; /* cached data, NULL if no cache */ +}; +#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) + +/* + * Usage: + * xprt = svcudp_create(sock); + * + * If sock<0 then a socket is created, else sock is used. + * If the socket, sock is not bound to a port then svcudp_create + * binds it to an arbitrary port. In any (successful) case, + * xprt->xp_sock is the registered socket number and xprt->xp_port is the + * associated port number. + * Once *xprt is initialized, it is registered as a transporter; + * see (svc.h, xprt_register). + * The routines returns NULL if a problem occurred. + */ +SVCXPRT * +svcudp_bufcreate(sock, sendsz, recvsz) + register int sock; + unsigned int sendsz, recvsz; +{ + bool_t madesock = FALSE; + register SVCXPRT *xprt; + register struct svcudp_data *su; + struct sockaddr_in addr; + int len = sizeof(struct sockaddr_in); + + if (sock == RPC_ANYSOCK) { + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("svcudp_create: socket creation problem"); + return ((SVCXPRT *)NULL); + } + madesock = TRUE; + } + memset((char *)&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + if (bindresvport(sock, &addr)) { + addr.sin_port = 0; + (void)bind(sock, (struct sockaddr *)&addr, len); + } + if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { + perror("svcudp_create - cannot getsockname"); + if (madesock) + (void)close(sock); + return ((SVCXPRT *)NULL); + } + xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT)); + if (xprt == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + su = (struct svcudp_data *)mem_alloc(sizeof(*su)); + if (su == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; + if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) { + (void)fprintf(stderr, "svcudp_create: out of memory\n"); + return (NULL); + } + xdrmem_create( + &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); + su->su_cache = NULL; + xprt->xp_p2 = (caddr_t)su; + xprt->xp_verf.oa_base = su->su_verfbody; + xprt->xp_ops = &svcudp_op; + xprt->xp_port = ntohs(addr.sin_port); + xprt->xp_sock = sock; + xprt_register(xprt); + return (xprt); +} + +SVCXPRT * +svcudp_create(sock) + int sock; +{ + + return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); +} + +static enum xprt_stat +svcudp_stat(xprt) + SVCXPRT *xprt; +{ + + return (XPRT_IDLE); +} + +static bool_t +svcudp_recv(xprt, msg) + register SVCXPRT *xprt; + struct rpc_msg *msg; +{ + register struct svcudp_data *su = su_data(xprt); + register XDR *xdrs = &(su->su_xdrs); + register int rlen; + char *reply; + rpc_u_int32 replylen; + + again: + xprt->xp_addrlen = sizeof(struct sockaddr_in); + rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, + 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); + if (rlen == -1 && errno == EINTR) + goto again; + if (rlen < (int) 4*sizeof(rpc_u_int32)) + return (FALSE); + xdrs->x_op = XDR_DECODE; + XDR_SETPOS(xdrs, 0); + if (! xdr_callmsg(xdrs, msg)) + return (FALSE); + su->su_xid = msg->rm_xid; + if (su->su_cache != NULL) { + if (cache_get(xprt, msg, &reply, &replylen)) { + (void) sendto(xprt->xp_sock, reply, (int) replylen, 0, + (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen); + return (TRUE); + } + } + return (TRUE); +} + +static bool_t svcudp_reply(xprt, msg) + register SVCXPRT *xprt; + struct rpc_msg *msg; +{ + register struct svcudp_data *su = su_data(xprt); + register XDR *xdrs = &(su->su_xdrs); + register int slen; + register bool_t stat = FALSE; + + xdrproc_t xdr_results; + caddr_t xdr_location; + bool_t has_args; + + if (msg->rm_reply.rp_stat == MSG_ACCEPTED && + msg->rm_reply.rp_acpt.ar_stat == SUCCESS) { + has_args = TRUE; + xdr_results = msg->acpted_rply.ar_results.proc; + xdr_location = msg->acpted_rply.ar_results.where; + + msg->acpted_rply.ar_results.proc = xdr_void; + msg->acpted_rply.ar_results.where = NULL; + } else + has_args = FALSE; + + xdrs->x_op = XDR_ENCODE; + XDR_SETPOS(xdrs, 0); + msg->rm_xid = su->su_xid; + if (xdr_replymsg(xdrs, msg) && + (!has_args || + (SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) { + slen = (int)XDR_GETPOS(xdrs); + if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, + (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) + == slen) { + stat = TRUE; + if (su->su_cache && slen >= 0) { + cache_set(xprt, (rpc_u_int32) slen); + } + } + } + return (stat); +} + +static bool_t +svcudp_getargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + return (SVCAUTH_UNWRAP(xprt->xp_auth, &(su_data(xprt)->su_xdrs), + xdr_args, args_ptr)); +} + +static bool_t +svcudp_freeargs(xprt, xdr_args, args_ptr) + SVCXPRT *xprt; + xdrproc_t xdr_args; + caddr_t args_ptr; +{ + register XDR *xdrs = &(su_data(xprt)->su_xdrs); + + xdrs->x_op = XDR_FREE; + return ((*xdr_args)(xdrs, args_ptr)); +} + +static void +svcudp_destroy(xprt) + register SVCXPRT *xprt; +{ + register struct svcudp_data *su = su_data(xprt); + + xprt_unregister(xprt); + (void)close(xprt->xp_sock); + XDR_DESTROY(&(su->su_xdrs)); + mem_free(rpc_buffer(xprt), su->su_iosz); + mem_free((caddr_t)su, sizeof(struct svcudp_data)); + mem_free((caddr_t)xprt, sizeof(SVCXPRT)); +} + + +/***********this could be a separate file*********************/ + +/* + * Fifo cache for udp server + * Copies pointers to reply buffers into fifo cache + * Buffers are sent again if retransmissions are detected. + */ + +#define SPARSENESS 4 /* 75% sparse */ + +#define CACHE_PERROR(msg) \ + (void) fprintf(stderr,"%s\n", msg) + +#define ALLOC(type, size) \ + (type *) mem_alloc((unsigned) (sizeof(type) * (size))) + +#define BZERO(addr, type, size) \ + memset((char *) addr, 0, sizeof(type) * (int) (size)) + +/* + * An entry in the cache + */ +typedef struct cache_node *cache_ptr; +struct cache_node { + /* + * Index into cache is xid, proc, vers, prog and address + */ + rpc_u_int32 cache_xid; + rpc_u_int32 cache_proc; + rpc_u_int32 cache_vers; + rpc_u_int32 cache_prog; + struct sockaddr_in cache_addr; + /* + * The cached reply and length + */ + char * cache_reply; + rpc_u_int32 cache_replylen; + /* + * Next node on the list, if there is a collision + */ + cache_ptr cache_next; +}; + + + +/* + * The entire cache + */ +struct udp_cache { + rpc_u_int32 uc_size; /* size of cache */ + cache_ptr *uc_entries; /* hash table of entries in cache */ + cache_ptr *uc_fifo; /* fifo list of entries in cache */ + rpc_u_int32 uc_nextvictim; /* points to next victim in fifo list */ + rpc_u_int32 uc_prog; /* saved program number */ + rpc_u_int32 uc_vers; /* saved version number */ + rpc_u_int32 uc_proc; /* saved procedure number */ + struct sockaddr_in uc_addr; /* saved caller's address */ +}; + + +/* + * the hashing function + */ +#define CACHE_LOC(transp, xid) \ + (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size)) + + +/* + * Enable use of the cache. + * Note: there is no disable. + */ +svcudp_enablecache(transp, size) + SVCXPRT *transp; + rpc_u_int32 size; +{ + struct svcudp_data *su = su_data(transp); + struct udp_cache *uc; + + if (su->su_cache != NULL) { + CACHE_PERROR("enablecache: cache already enabled"); + return(0); + } + uc = ALLOC(struct udp_cache, 1); + if (uc == NULL) { + CACHE_PERROR("enablecache: could not allocate cache"); + return(0); + } + uc->uc_size = size; + uc->uc_nextvictim = 0; + uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); + if (uc->uc_entries == NULL) { + CACHE_PERROR("enablecache: could not allocate cache data"); + return(0); + } + BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); + uc->uc_fifo = ALLOC(cache_ptr, size); + if (uc->uc_fifo == NULL) { + CACHE_PERROR("enablecache: could not allocate cache fifo"); + return(0); + } + BZERO(uc->uc_fifo, cache_ptr, size); + su->su_cache = (char *) uc; + return(1); +} + + +/* + * Set an entry in the cache + */ +static void +cache_set(xprt, replylen) + SVCXPRT *xprt; + rpc_u_int32 replylen; +{ + register cache_ptr victim; + register cache_ptr *vicp; + register struct svcudp_data *su = su_data(xprt); + struct udp_cache *uc = (struct udp_cache *) su->su_cache; + unsigned int loc; + char *newbuf; + + /* + * Find space for the new entry, either by + * reusing an old entry, or by mallocing a new one + */ + victim = uc->uc_fifo[uc->uc_nextvictim]; + if (victim != NULL) { + loc = CACHE_LOC(xprt, victim->cache_xid); + for (vicp = &uc->uc_entries[loc]; + *vicp != NULL && *vicp != victim; + vicp = &(*vicp)->cache_next) + ; + if (*vicp == NULL) { + CACHE_PERROR("cache_set: victim not found"); + return; + } + *vicp = victim->cache_next; /* remote from cache */ + newbuf = victim->cache_reply; + } else { + victim = ALLOC(struct cache_node, 1); + if (victim == NULL) { + CACHE_PERROR("cache_set: victim alloc failed"); + return; + } + newbuf = mem_alloc(su->su_iosz); + if (newbuf == NULL) { + CACHE_PERROR("cache_set: could not allocate new rpc_buffer"); + return; + } + } + + /* + * Store it away + */ + victim->cache_replylen = replylen; + victim->cache_reply = rpc_buffer(xprt); + rpc_buffer(xprt) = newbuf; + xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE); + victim->cache_xid = su->su_xid; + victim->cache_proc = uc->uc_proc; + victim->cache_vers = uc->uc_vers; + victim->cache_prog = uc->uc_prog; + victim->cache_addr = uc->uc_addr; + loc = CACHE_LOC(xprt, victim->cache_xid); + victim->cache_next = uc->uc_entries[loc]; + uc->uc_entries[loc] = victim; + uc->uc_fifo[uc->uc_nextvictim++] = victim; + uc->uc_nextvictim %= uc->uc_size; +} + +/* + * Try to get an entry from the cache + * return 1 if found, 0 if not found + */ +static int +cache_get(xprt, msg, replyp, replylenp) + SVCXPRT *xprt; + struct rpc_msg *msg; + char **replyp; + rpc_u_int32 *replylenp; +{ + unsigned int loc; + register cache_ptr ent; + register struct svcudp_data *su = su_data(xprt); + register struct udp_cache *uc = (struct udp_cache *) su->su_cache; + +# define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0) + + loc = CACHE_LOC(xprt, su->su_xid); + for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { + if (ent->cache_xid == su->su_xid && + ent->cache_proc == uc->uc_proc && + ent->cache_vers == uc->uc_vers && + ent->cache_prog == uc->uc_prog && + EQADDR(ent->cache_addr, uc->uc_addr)) { + *replyp = ent->cache_reply; + *replylenp = ent->cache_replylen; + return(1); + } + } + /* + * Failed to find entry + * Remember a few things so we can do a set later + */ + uc->uc_proc = msg->rm_call.cb_proc; + uc->uc_vers = msg->rm_call.cb_vers; + uc->uc_prog = msg->rm_call.cb_prog; + uc->uc_addr = xprt->xp_raddr; + return(0); +} + diff --git a/src/lib/rpc/types.hin b/src/lib/rpc/types.hin new file mode 100644 index 000000000..619a9ce20 --- /dev/null +++ b/src/lib/rpc/types.hin @@ -0,0 +1,90 @@ +/* @(#)types.h 2.3 88/08/15 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)types.h 1.18 87/07/24 SMI */ + +/* + * Rpc additions to + */ +#ifndef __TYPES_RPC_HEADER__ +#define __TYPES_RPC_HEADER__ + +#include + +/* this is a 32-bit int type */ + +#if @SIZEOF_INT@ == 4 +typedef int rpc_int32; +typedef unsigned int rpc_u_int32; +#elif @SIZEOF_LONG@ == 4 +typedef long rpc_int32; +typedef unsigned long rpc_u_int32; +#endif + +#define bool_t int +#define enum_t int +#ifndef FALSE +# define FALSE (0) +#endif +#ifndef TRUE +# define TRUE (1) +#endif +#define __dontcare__ -1 +#ifndef NULL +# define NULL 0 +#endif + +#if defined(__osf__) +#include +#endif +#define mem_alloc(bsize) (char *) malloc(bsize) +#define mem_free(ptr, bsize) free(ptr) + +#ifndef makedev /* ie, we haven't already included it */ +#include +#endif +#ifdef _AIX +#include +#endif +#include +#include +#include +#include /* XXX This should not have to be here. + * I got sick of seeing the warnings for MAXHOSTNAMELEN + * and the two values were different. -- shanzer + */ + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (rpc_u_int32)0x7F000001 +#endif +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#endif /* ndef __TYPES_RPC_HEADER__ */ diff --git a/src/lib/rpc/unit-test/Makefile b/src/lib/rpc/unit-test/Makefile new file mode 100644 index 000000000..31853cb35 --- /dev/null +++ b/src/lib/rpc/unit-test/Makefile @@ -0,0 +1,97 @@ +# Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. +# +# $Id$ +# $Source$ +# +# $Log$ +# Revision 1.9 1996/07/22 20:41:38 marc +# this commit includes all the changes on the OV_9510_INTEGRATION and +# OV_MERGE branches. This includes, but is not limited to, the new openvision +# admin system, and major changes to gssapi to add functionality, and bring +# the implementation in line with rfc1964. before committing, the +# code was built and tested for netbsd and solaris. +# +# Revision 1.8.4.1 1996/07/18 04:20:01 marc +# merged in changes from OV_9510_BP to OV_9510_FINAL1 +# +# Revision 1.8.2.1 1996/06/20 23:41:48 marc +# File added to the repository on a branch +# +# Revision 1.8 1995/12/07 17:36:54 jik +# Use "rpc_test" instead of "rpc-test", to avoid problems with rpcgen on +# some systems. See PR 3553. +# +# Revision 1.7 1995/10/02 08:02:49 jik +# Delete rpc-tset_clnt.c, rpc-test_svc.c and rpc-test.h before creating +# them, because rpcgen on some platforms won't output to a file that +# already exists. +# +# Revision 1.6 1994/10/24 19:35:57 bjaspan +# [secure-build/2649: cannot use -L when compiling] +# +# Sandbox: +# +# [secure-build/2649] don't use -L +# +# Revision 1.7 1994/10/11 20:06:14 bjaspan +# [secure-build/2649] don't use -L +# +# Revision 1.6 1994/09/30 22:25:29 jik +# Don't need to set MDFLAGS to -a anymore, because it's done +# automatically by the rules now. +# +# Revision 1.5 1994/03/22 19:55:34 shanzer +# change NETLIBS to NETLIB +# +# Revision 1.4 1994/03/18 17:47:00 shanzer +# added NETLIBS and BSDLIB +# +# Revision 1.3 1993/12/13 02:00:39 bjaspan +# recurse to testsuite subdir. +# +# Revision 1.2 1993/12/08 21:45:47 bjaspan +# misc +# +# Revision 1.1 1993/11/03 23:53:58 bjaspan +# Initial revision +# + +TOP = ../.. +include $(TOP)/config.mk/template + +SUBDIRS = testsuite + +expand SubdirTarget + +SRCS = client.c server.c + +CFLAGS := -I../.. -I. $(CFLAGS) + +#LIBS = ../librpclib.a $(LIBGSSAPI_TRUST) $(LIBDB) $(LIBCOM_ERR) $(LIBDYN) +LIBS = ../librpclib.a $(LIBGSSAPI_KRB5) $(LIBDB) $(LIBKRB5) \ + $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR) $(LIBDYN) $(BSDLIB) $(NETLIB) +DEPS = ../librpclib.a rpc_test.h +DEPENDS = rpc_test.h + +PROG = client +SRCS = client.c +OBJS = client.o rpc_test_clnt.o + +expand Program +expand Depend + +PROG = server +SRCS = server.c +OBJS = server.o rpc_test_svc.o + +expand Program +expand Depend + +rpc_test.h rpc_test_clnt.c rpc_test_svc.c: rpc_test.x + -rm -f rpc_test_clnt.c rpc_test_svc.c rpc_test.h + rpcgen -l rpc_test.x -o rpc_test_clnt.c + rpcgen -m rpc_test.x -o rpc_test_svc.c + rpcgen -h rpc_test.x -o rpc_test.h + +clean:: + rm -f rpc_test.h rpc_test_clnt.c rpc_test_svc.c diff --git a/src/lib/rpc/unit-test/client.c b/src/lib/rpc/unit-test/client.c new file mode 100644 index 000000000..bcf2700e5 --- /dev/null +++ b/src/lib/rpc/unit-test/client.c @@ -0,0 +1,320 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.13 1996/07/22 20:41:40 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.12.4.1 1996/07/18 04:20:03 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.12.2.1 1996/06/20 23:41:56 marc + * File added to the repository on a branch + * + * Revision 1.12 1996/05/12 06:58:10 marc + * type renamings for compatibility with beta6 + * + * Revision 1.11 1996/02/12 15:58:42 grier + * [secure/3570] + * long conversion + * + * Revision 1.10 1995/12/07 17:37:03 jik + * Use "rpc_test" instead of "rpc-test", to avoid problems with rpcgen on + * some systems. See PR 3553. + * + * Revision 1.9 1994/09/21 18:38:56 bjaspan + * [secure-rpc/2536: unit test client.c: memory initialization and out-of-bounds reference bugs] + * [secure-releng/2537: audit secure-rpc/2536: minor memory problems in unit-test client] + * + * Sandbox: + * + * 1. Don't allow the count specifie on the command line to be bigger + * than the size of the buffer use for testing. + * 2. When initializing the buffer for the lengths test, initialize it to + * count bytes. + * + * Revision 1.9 1994/09/19 01:28:04 root + * 1. Don't allow the count specifie on the command line to be bigger + * than the size of the buffer use for testing. + * 2. When initializing the buffer for the lengths test, initialize it to + * count bytes. + * + * Revision 1.8 1994/04/06 22:13:01 jik + * Change -auth_once to -o, add -a, -m and -s arguments to set + * auth_debug_gssapi, svc_debug_gssapi and misc_debug_gssapi variables. + * + * Revision 1.7 1994/04/05 20:50:09 bjaspan + * fix typo that causes coredump when server blocks/fails + * + * Revision 1.6 1993/12/08 21:44:45 bjaspan + * test fix for secure-rpc/586, improve arg handlng + * + * Revision 1.5 1993/12/06 21:23:30 bjaspan + * accept count arg for RPC_TEST_LENGTHS + * + * Revision 1.4 1993/12/01 23:41:45 bjaspan + * don't free echo_resp if call fails + * + * Revision 1.3 1993/11/15 19:53:09 bjaspan + * test auto-syncrhonization + * + * Revision 1.2 1993/11/12 02:33:43 bjaspan + * use clnt_pcreateerror for auth failures + * + * Revision 1.1 1993/11/03 23:53:58 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include +#include "rpc_test.h" + +#define BIG_BUF 4096 +/* copied from auth_gssapi.c for hackery */ +struct auth_gssapi_data { + bool_t established; + CLIENT *clnt; + gss_ctx_id_t context; + gss_buffer_desc client_handle; + OM_uint32 seq_num; + int def_cred; + + /* pre-serialized ah_cred */ + u_char cred_buf[MAX_AUTH_BYTES]; + rpc_int32 cred_len; +}; +#define AUTH_PRIVATE(auth) ((struct auth_gssapi_data *)auth->ah_private) + +extern int auth_debug_gssapi; +char *whoami; + +main(argc, argv) + int argc; + char **argv; +{ + char *host, *target, *echo_arg, **echo_resp, buf[BIG_BUF]; + CLIENT *clnt; + AUTH *tmp_auth; + struct rpc_err e; + int i, count, auth_once; + extern int optind; + extern char *optarg; + extern int svc_debug_gssapi, misc_debug_gssapi, auth_debug_gssapi; + int c; + + whoami = argv[0]; + count = 1026; + auth_once = 0; + + while ((c = getopt(argc, argv, "a:m:os:")) != -1) { + switch (c) { + case 'a': + auth_debug_gssapi = atoi(optarg); + break; + case 'm': + misc_debug_gssapi = atoi(optarg); + break; + case 'o': + auth_once++; + break; + case 's': + svc_debug_gssapi = atoi(optarg); + break; + case '?': + usage(); + break; + } + } + + argv += optind; + argc -= optind; + + switch (argc) { + case 3: + count = atoi(argv[2]); + if (count > BIG_BUF) { + fprintf(stderr, "Test count cannot exceed %d.\n", BIG_BUF); + usage(); + } + case 2: + host = argv[0]; + target = argv[1]; + break; + default: + usage(); + } + + /* client handle to rstat */ + clnt = clnt_create(host, RPC_TEST_PROG, RPC_TEST_VERS_1, "tcp"); + if (clnt == NULL) { + clnt_pcreateerror(whoami); + exit(1); + } + + clnt->cl_auth = auth_gssapi_create_default(clnt, target); + if (clnt->cl_auth == NULL) { + clnt_pcreateerror(whoami); + exit(2); + } + + /* + * Call the echo service multiple times. + */ + echo_arg = buf; + for (i = 0; i < 3; i++) { + sprintf(buf, "testing %d\n", i); + + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) { + fprintf(stderr, "RPC_TEST_ECHO call %d%s", i, + clnt_sperror(clnt, "")); + } + if (strncmp(*echo_resp, "Echo: ", 6) && + strcmp(echo_arg, (*echo_resp) + 6) != 0) + fprintf(stderr, "RPC_TEST_ECHO call %d response wrong: " + "arg = %s, resp = %s\n", echo_arg, *echo_resp); + xdr_free(xdr_wrapstring, echo_resp); + } + + /* + * Make a call with an invalid verifier and check for error; + * server should log error message. It is important to + *increment* seq_num here, since a decrement would be fixed (see + * below). Note that seq_num will be incremented (by + * authg_gssapi_refresh) twice, so we need to decrement by three + * to reset. + */ + AUTH_PRIVATE(clnt->cl_auth)->seq_num++; + + echo_arg = "testing with bad verf"; + + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) { + CLNT_GETERR(clnt, &e); + if (e.re_status != RPC_AUTHERROR || e.re_why != AUTH_REJECTEDVERF) + clnt_perror(clnt, whoami); + } else { + fprintf(stderr, "bad seq didn't cause failure\n"); + } + + AUTH_PRIVATE(clnt->cl_auth)->seq_num -= 3; + + /* + * Make sure we're resyncronized. + */ + echo_arg = "testing for reset"; + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) + clnt_perror(clnt, "Sequence number improperly reset"); + + /* + * Now simulate a lost server response, and see if + * auth_gssapi_refresh recovers. + */ + AUTH_PRIVATE(clnt->cl_auth)->seq_num--; + echo_arg = "forcing auto-resynchronization"; + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) + clnt_perror(clnt, "Auto-resynchronization failed"); + + /* + * Now make sure auto-resyncrhonization actually worked + */ + echo_arg = "testing for resynchronization"; + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) + clnt_perror(clnt, "Auto-resynchronization did not work"); + + /* + * Test fix for secure-rpc/586, part 1: btree keys must be + * unique. Create another context from the same credentials; it + * should have the same expiration time and will cause the server + * to abort if the clients are not differentiated. + * + * Test fix for secure-rpc/586, part 2: btree keys cannot be + * mutated in place. To test this: a second client, *with a + * later expiration time*, must be run. The second client should + * destroy itself *after* the first one; if the key-mutating bug + * is not fixed, the second client_data will be in the btree + * before the first, but its key will be larger; thus, when the + * first client calls AUTH_DESTROY, the server won't find it in + * the btree and call abort. + * + * For unknown reasons, running just a second client didn't + * tickle the bug; the btree code seemed to guess which node to + * look at first. Running a total of three clients does ticket + * the bug. Thus, the full test sequence looks like this: + * + * kinit -l 20m user && client server test@ddn 200 + * sleep 1 + * kini -l 30m user && client server test@ddn 300 + * sleep 1 + * kinit -l 40m user && client server test@ddn 400 + */ + if (! auth_once) { + tmp_auth = clnt->cl_auth; + clnt->cl_auth = auth_gssapi_create_default(clnt, target); + if (clnt->cl_auth == NULL) { + clnt_pcreateerror(whoami); + exit(2); + } + AUTH_DESTROY(clnt->cl_auth); + clnt->cl_auth = tmp_auth; + } + + /* + * Try RPC calls with argument/result lengths [0, 1025]. Do + * this last, since it takes a while.. + */ + echo_arg = buf; + memset(buf, 0, count); + for (i = 0; i < count; i++) { + echo_resp = rpc_test_echo_1(&echo_arg, clnt); + if (echo_resp == NULL) { + fprintf(stderr, "RPC_TEST_LENGTHS call %d%s", i, + clnt_sperror(clnt, "")); + break; + } else { + if (strncmp(*echo_resp, "Echo: ", 6) && + strcmp(echo_arg, (*echo_resp) + 6) != 0) + fprintf(stderr, + "RPC_TEST_LENGTHS call %d response wrong\n"); + xdr_free(xdr_wrapstring, echo_resp); + } + + /* cycle from 1 to 255 */ + buf[i] = (i % 255) + 1; + + if (i % 100 == 0) { + fputc('.', stdout); + fflush(stdout); + } + } + fputc('\n', stdout); + + AUTH_DESTROY(clnt->cl_auth); + CLNT_DESTROY(clnt); + exit(0); +} + +usage() +{ + fprintf(stderr, "usage: %s [-a] [-s num] [-m num] host service [count]\n", + whoami); + exit(1); +} diff --git a/src/lib/rpc/unit-test/rpc_test.x b/src/lib/rpc/unit-test/rpc_test.x new file mode 100644 index 000000000..e9ae27c97 --- /dev/null +++ b/src/lib/rpc/unit-test/rpc_test.x @@ -0,0 +1,30 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.2 1996/07/22 20:41:42 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.1.4.1 1996/07/18 04:20:04 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * +# Revision 1.1.2.1 1996/06/20 23:42:06 marc +# File added to the repository on a branch +# +# Revision 1.1 1993/11/03 23:53:58 bjaspan +# Initial revision +# + */ + +program RPC_TEST_PROG { + version RPC_TEST_VERS_1 { + string RPC_TEST_ECHO(string) = 1; + } = 1; +} = 1000001; diff --git a/src/lib/rpc/unit-test/server.c b/src/lib/rpc/unit-test/server.c new file mode 100644 index 000000000..d98b2df0b --- /dev/null +++ b/src/lib/rpc/unit-test/server.c @@ -0,0 +1,306 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.15 1996/07/22 20:41:44 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.14.4.1 1996/07/18 04:20:06 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.14.2.1 1996/06/20 23:42:16 marc + * File added to the repository on a branch + * + * Revision 1.14 1996/05/12 06:59:06 marc + * change SERVICE_NAME to "host" instead of "server" + * + * remove KRB5KTNAME support, since the library supports it internally now. + * + * Revision 1.13 1995/12/07 17:36:58 jik + * Use "rpc_test" instead of "rpc-test", to avoid problems with rpcgen on + * some systems. See PR 3553. + * + * Revision 1.12 1995/11/07 23:20:44 grier + * Add stdlib.h + * Add string.h + * + * Revision 1.11 1995/03/24 19:55:28 jik + * Cast a const gss_OID to (gss_OID) to prevent a compiler warning. + * + * Revision 1.10 1995/02/22 15:54:17 jik + * I was a moron in revision 1.8. This is the server function, not the + * client function, so it gets a struct svc_req *, not a CLIENT *. + * + * Revision 1.9 1995/02/22 15:21:51 jik + * Linux's rpcgen names the server function differently from the client + * function ("_svc" is appended to the end of it). + * + * Revision 1.8 1995/02/22 14:35:05 jik + * RPC server functions have CLIENT * passed into them, so I added it as + * an argument to rpc_test_echo_1. + * + * Revision 1.7 1994/09/21 18:35:57 bjaspan + * [gssapi/438: gss_nt_service_name should default to local host] + * [secure-releng/2513: audit gssapi/438: gss_nt_service_name should default to local host] + * + * Sandbox: + * + * Don't need to get local host name and put it in the service name, + * since the gssapi library does that now. See PR 438. + * + * Revision 1.8 1994/09/01 17:21:59 jik + * Don't need to get local host name and put it in the service name, + * since the gssapi library does that now. See PR 438. + * + * Revision 1.7 1994/04/08 17:22:11 bjaspan + * add KRB5KTNAME hack so unit tests continue to work + * + * Revision 1.6 1994/04/05 20:50:26 bjaspan + * print "running" when ready to tests can proceed + * + * Revision 1.5 1994/04/05 19:49:54 jik + * Use host name instead of localhost. + * + * Revision 1.4 1994/03/08 00:14:58 shanzer + * changed call to inet_ntoa + * + * Revision 1.3 1993/12/13 01:37:54 bjaspan + * update for new test system + * ,. + * + * Revision 1.2 1993/12/08 21:45:16 bjaspan + * display badauth errors, improve arg handling + * + * Revision 1.1 1993/11/03 23:53:58 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include +#include +#include /* inet_ntoa */ +#include +#include +#include +#include /* MAXHOSTNAMELEN */ +#include "rpc_test.h" + +#ifdef linux +/* + For some reason, Linux's rpcgen names the server function + differently from the client function. I suppose this is useful if + you want to include them both in the same library or something, but + not useful at all if you want to link the client code directly to + the server code for testing, instead of going through the RPC layer. + */ +#define rpc_test_echo_1 rpc_test_echo_1_svc +#endif + +extern void rpc_test_prog_1(); + +extern int svc_debug_gssapi, misc_debug_gssapi; + +void rpc_test_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, void *data); +void log_badauth_display_status(OM_uint32 major, OM_uint32 minor); +void log_badauth_display_status_1(OM_uint32 code, int type, int rec); +static void rpc_test_badverf(gss_name_t client, gss_name_t server, + struct svc_req *rqst, struct rpc_msg *msg, + caddr_t data); + +#ifndef SERVICE_NAME +#define SERVICE_NAME "host" +#endif + +main(int argc, char **argv) +{ + auth_gssapi_name names[2]; + register SVCXPRT *transp; + + names[0].name = SERVICE_NAME; + names[0].type = (gss_OID) gss_nt_service_name; + names[1].name = 0; + names[1].type = 0; + + switch (argc) { + case 3: + misc_debug_gssapi = atoi(argv[2]); + case 2: + svc_debug_gssapi = atoi(argv[1]); + case 1: + break; + default: + fprintf(stderr, "Usage: server [svc-debug] [misc-debug]\n"); + exit(1); + } + + (void) pmap_unset(RPC_TEST_PROG, RPC_TEST_VERS_1); + + transp = svctcp_create(RPC_ANYSOCK, 0, 0); + if (transp == NULL) { + fprintf(stderr, "cannot create tcp service."); + exit(1); + } + if (!svc_register(transp, RPC_TEST_PROG, RPC_TEST_VERS_1, + rpc_test_prog_1, IPPROTO_TCP)) { + fprintf(stderr, + "unable to register (RPC_TEST_PROG, RPC_TEST_VERS_1, tcp)."); + exit(1); + } + + if (_svcauth_gssapi_set_names(names, 0) == FALSE) { + fprintf(stderr, "unable to set gssapi names\n"); + exit(1); + } + + _svcauth_gssapi_set_log_badauth_func(rpc_test_badauth, NULL); + _svcauth_gssapi_set_log_badverf_func(rpc_test_badverf, NULL); + + printf("running\n"); + + svc_run(); + fprintf(stderr, "svc_run returned"); + exit(1); + /* NOTREACHED */ +} + +char **rpc_test_echo_1(char **arg, struct svc_req *h) +{ + static char *res = NULL; + + if (res) + free(res); + res = (char *) malloc(strlen(*arg) + strlen("Echo: ") + 1); + sprintf(res, "Echo: %s", *arg); + return &res; +} + +static void rpc_test_badverf(gss_name_t client, gss_name_t server, + struct svc_req *rqst, struct rpc_msg *msg, + caddr_t data) +{ + OM_uint32 minor_stat; + gss_OID type; + gss_buffer_desc client_name, server_name; + + (void) gss_display_name(&minor_stat, client, &client_name, &type); + (void) gss_display_name(&minor_stat, server, &server_name, &type); + + printf("rpc_test server: bad verifier from %s at %s:%d for %s\n", + client_name.value, + inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr), + ntohs(rqst->rq_xprt->xp_raddr.sin_port), + server_name.value); + + (void) gss_release_buffer(&minor_stat, &client_name); + (void) gss_release_buffer(&minor_stat, &server_name); +} + +/* + * Function: log_badauth + * + * Purpose: Callback from GSS-API Sun RPC for authentication + * failures/errors. + * + * Arguments: + * major (r) GSS-API major status + * minor (r) GSS-API minor status + * addr (r) originating address + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the GSS-API error to stdout. + */ +void rpc_test_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, void *data) +{ + char *a; + + /* Authentication attempt failed: , */ + + a = inet_ntoa(addr->sin_addr); + + printf("rpc_test server: Authentication attempt failed: %s", a); + log_badauth_display_status(major, minor); + printf("\n"); +} + +void log_badauth_display_status(OM_uint32 major, OM_uint32 minor) +{ + log_badauth_display_status_1(major, GSS_C_GSS_CODE, 0); + log_badauth_display_status_1(minor, GSS_C_MECH_CODE, 0); +} + +void log_badauth_display_status_1(OM_uint32 code, int type, int rec) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc msg; + int msg_ctx; + + msg_ctx = 0; + while (1) { + gssstat = gss_display_status(&minor_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (gssstat != GSS_S_COMPLETE) { + if (!rec) { + log_badauth_display_status_1(gssstat,GSS_C_GSS_CODE,1); + log_badauth_display_status_1(minor_stat, + GSS_C_MECH_CODE, 1); + } else + printf("GSS-API authentication error %s: " + "recursive failure!\n", msg); + return; + } + + printf(", %s", (char *)msg.value); + (void) gss_release_buffer(&minor_stat, &msg); + + if (!msg_ctx) + break; + } +} + + +#if 0 + +/* this hack is no longer necessary, since the library supports it + internally */ + +/* This is a hack to change the default keytab name */ + +#include +extern char *krb5_defkeyname; + +krb5_error_code +krb5_kt_default_name(char *name, int namesize) +{ + char *ktname; + + if ((ktname = getenv("KRB5KTNAME")) == NULL) + ktname = krb5_defkeyname; + + if (namesize < strlen(ktname)+1) + return(KRB5_CONFIG_NOTENUFSPACE); + + strcpy(name, ktname); + + return(0); +} + +#endif diff --git a/src/lib/rpc/unit-test/testsuite/Makefile b/src/lib/rpc/unit-test/testsuite/Makefile new file mode 100644 index 000000000..8a2cccdc2 --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/Makefile @@ -0,0 +1,24 @@ +# +# $Id$ +# + +TOP = ../../.. +include $(TOP)/config.mk/template + +export RPC_TEST_SRVTAB := /tmp/rpc_test_v5srvtab + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS) + ./rpc_test_setup.sh + +unit-test-body:: + $(RUNTEST) SERVER=../server CLIENT=../client --tool rpc_test + +unit-test-cleanup:: + $(STOP_SERVERS) + -rm -f /tmp/rpc_test_v5srvtab /tmp/krb5cc_rpc_test_fullrun + +clean:: + $(CLEAN) rpc_test.log rpc_test.plog rpc_test.sum rpc_test.psum diff --git a/src/lib/rpc/unit-test/testsuite/config/unix.exp b/src/lib/rpc/unit-test/testsuite/config/unix.exp new file mode 100644 index 000000000..030837d49 --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/config/unix.exp @@ -0,0 +1,79 @@ +# +# $Id$ +# + +set kill /bin/kill +set sleep /bin/sleep +set kinit $env(TOP)/install/bin/kinit +set kdestroy $env(TOP)/install/bin/kdestroy + +set hostname [exec hostname] + +proc rpc_test_version {} { + global CLIENT + global SERVER + + clone_output "$CLIENT version " + clone_output "$SERVER version " +} + +proc rpc_test_load {} { + # +} + +# rpc_test_exit -- clean up and exit +proc rpc_test_exit {} { + global server_id + global server_pid + global server_started + global kill + + if {[catch { + expect { + -i $server_id + eof { + fail "server exited!" + verbose $expect_out(buffer) 1 + } + timeout { pass "server survived" } + } + } tmp]} { + fail "server exited! (expect failed)" + } +} + +# +# rpc_test_start -- start the rpc_test server running +# +proc rpc_test_start { } { + global SERVER + global server_id + global server_pid + global server_started + global env + + set env(KRB5KTNAME) FILE:$env(RPC_TEST_SRVTAB) + + verbose "% $SERVER" 1 + set server_pid [spawn $SERVER] + set server_id $spawn_id + + unset env(KRB5KTNAME) + + set timeout 30 + + expect { + "running" { } + eof { + fail "server exited!" + verbose $expect_out(buffer) 1 + } + timeout { + fail "server didn't start in $timeout seconds" + verbose $expect_out(buffer) 1 + } + } + +} + +rpc_test_start diff --git a/src/lib/rpc/unit-test/testsuite/helpers.exp b/src/lib/rpc/unit-test/testsuite/helpers.exp new file mode 100644 index 000000000..1a37ad512 --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/helpers.exp @@ -0,0 +1,128 @@ +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + + +proc kinit {princ pass lifetime} { + global kinit + global wait_error_index wait_errno_index wait_status_index + + spawn -noecho $kinit -l $lifetime $princ + expect { + -re "Password for $princ.*: " { send "$pass\n" } + timeout { error "Timeout waiting for kinit"; close } + } + expect { eof {} } + + set ret [wait] + if {[lindex $ret $wait_error_index] == -1} { + error \ + "wait(kinit $princ) returned error [lindex $ret $wait_errno_index]" + } else { + if {[lindex $ret $wait_status_index] != 0} { + error \ + "kinit $princ failed with [lindex $ret $wait_status_index]" + } + } +} + +proc flush_server {} { + global server_id + global expect_out + + verbose "flushing server output" 1 + + while {1} { + set timeout 5 + + expect { + -i $server_id + -re "^.+$" { + verbose "server output: $expect_out(buffer)" + } + timeout { break } + } + } +} + +proc start_client {testname ccname user password lifetime count + {target ""}} { + global env + global CLIENT + global hostname + global spawn_id + global verbose + + if {$target == ""} { + set target "server@$hostname" + } + + set env(KRB5CCNAME) FILE:/tmp/krb5cc_rpc_test_$ccname + kinit $user $password $lifetime + + if {$verbose > 0} { + spawn $CLIENT -a 1 -s 1 -m 1 $hostname $target $count + } else { + spawn $CLIENT $hostname $target $count + } + + verbose "$testname: client $ccname started" + + unset env(KRB5CCNAME) +} + +proc eof_client {testname ccname id status} { + verbose "$testname: eof'ing for client $ccname" 1 + + expect { + -i $id + eof { verbose $expect_out(buffer) 1 } + timeout { + fail "$testname: timeout waiting for client $ccname to exit" + } + } + wait_client $testname $ccname $id $status +} + + +proc wait_client {testname ccname id status} { + global env + global kill + global kdestroy + global wait_error_index wait_errno_index wait_status_index + + verbose "$testname: waiting for client $ccname" 1 + + set ret [wait -i $id] + if {[lindex $ret $wait_error_index] == -1} { + fail \ + "$testname: wait $ccname returned error [lindex $ret $wait_errno_index]" + } else { + if {[lindex $ret $wait_status_index] == $status} { + pass "$testname: client $ccname" + } else { + fail "$testname: client $ccname: unexpected return status [lindex $ret $wait_status_index], should be $status." + } + } + + set env(KRB5CCNAME) FILE:/tmp/krb5cc_rpc_test_$ccname + if {[catch "exec $kdestroy"] != 0} { + error "$testname: cannot destroy client $ccname ccache" + } + + unset env(KRB5CCNAME) +} diff --git a/src/lib/rpc/unit-test/testsuite/rpc_test.0/expire.exp b/src/lib/rpc/unit-test/testsuite/rpc_test.0/expire.exp new file mode 100644 index 000000000..d80bae6da --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/rpc_test.0/expire.exp @@ -0,0 +1,21 @@ +set timeout 40 + +load_lib "helpers.exp" + +global spawn_id + +start_client expire 1 testuser notathena 20m 100 +set client1_id $spawn_id +flush_server + +start_client expire 2 testuser notathena 40m 300 +set client2_id $spawn_id +flush_server + +start_client expire 3 testuser notathena 60m 500 +set client3_id $spawn_id +flush_server + +eof_client expire 1 $client1_id 0 +eof_client expire 2 $client2_id 0 +eof_client expire 3 $client3_id 0 diff --git a/src/lib/rpc/unit-test/testsuite/rpc_test.0/fullrun.exp b/src/lib/rpc/unit-test/testsuite/rpc_test.0/fullrun.exp new file mode 100644 index 000000000..6e8acf80e --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/rpc_test.0/fullrun.exp @@ -0,0 +1,95 @@ +set timeout 120 + +load_lib "helpers.exp" + +global spawn_id +global server_id + +# Start the client and do a full run +start_client "full run" fullrun testuser notathena 8 1026 +set client_id $spawn_id + +# +# test: did we get 11 dots? +# +verbose "Starting RPC echo test. This will take about 50 seconds.\n" + +set ver_line "rpc_test server: bad verifier\[^\r\n\]*\n" + +set dots 0 +set server_lines 0 +while {1} { + set oldtimeout $timeout + set timeout 5 + while {1} { + expect { + -i $server_id + -re $ver_line { + verbose "Got line from server." + incr server_lines + } + default { + break + } + } + } + set timeout $oldtimeout + expect { + -i $client_id + . { + incr dots + verbose "$expect_out(buffer)" 1 + if ($dots==11) { break } + } + eof { + # + # test: was the exit status right? + # + wait_client "full run" fullrun $client_id 0 + break + } + + timeout { + verbose "Timeout waiting for dot\n" 1 + fail "full run: timeout waiting for dot" + break + } + + } +} +if {$dots==11} { + pass "fullrun: echo test" +} else { + fail "fullrun: echo test: expected 11 dots, got $dots" +} + +# +# test: server logged four bad verifiers? +# +verbose "full run: checking server output" + +# Small timeout, since the server should have already printed everything +set timeout 5 + +while {$server_lines < 4} { + expect { + -i $server_id + -re $ver_line { + incr server_lines + } + -re ".+\r\n" { + verbose "Unexpected server output: $expect_out(buffer)" + } + default { + break + } + } +} + +if {$server_lines == 4} { + pass "fullrun: bad verifiers" +} else { + fail "fullrun: expected four bad verifiers, got $server_lines" +} + +flush_server diff --git a/src/lib/rpc/unit-test/testsuite/rpc_test.0/gsserr.exp b/src/lib/rpc/unit-test/testsuite/rpc_test.0/gsserr.exp new file mode 100644 index 000000000..f9d53052d --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/rpc_test.0/gsserr.exp @@ -0,0 +1,27 @@ +set timeout 30 + +load_lib "helpers.exp" + +global spawn_id +global server_id +global hostname + +start_client "gss err" gsserr testuser notathena 8 1026 notserver@$hostname + +eof_client "gss err" gsserr $spawn_id 2 + +# +# test: server logged an authentication attempted failed? +# +verbose "gss err: checking server output" + +expect { + -i $server_id + -re "rpc_test server: Authent.*failed: .* Wrong princ" { + pass "gss err: server logged auth error" + } + eof { fail "gss err: server exited" } + timeout { fail "gss err: timeout waiting for server output" } +} + +flush_server diff --git a/src/lib/rpc/unit-test/testsuite/rpc_test_setup.sh b/src/lib/rpc/unit-test/testsuite/rpc_test_setup.sh new file mode 100644 index 000000000..2a97af4d3 --- /dev/null +++ b/src/lib/rpc/unit-test/testsuite/rpc_test_setup.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# This script performs additional setup for the RPC unit test. It +# assumes that gmake has put TOP and RPC_TEST_SRVTAB into the +# environment. +# +# $Id$ +# $Source$ + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${CLNTTCL=$TESTDIR/util/ovsec_kadm_clnt_tcl} +DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL +DUMMY=${MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +PATH=$TOP/install/admin:$PATH; export PATH + +CANON_HOST=`perl -e 'chop($_=\`hostname\`);($n,$a,$t,$l,@a)=gethostbyname($_);($_)=gethostbyaddr($a[0],$t); print;'` +export CANON_HOST + +eval $CLNTTCL <<'EOF' $REDIRECT +source $env(TCLUTIL) +set h $env(CANON_HOST) +puts stdout [ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle] +puts stdout [ovsec_kadm_create_principal $server_handle [simple_principal server/$h] {OVSEC_KADM_PRINCIPAL} admin] +puts stdout [ovsec_kadm_randkey_principal $server_handle server/$h key] +puts stdout [ovsec_kadm_create_principal $server_handle [simple_principal notserver/$h] {OVSEC_KADM_PRINCIPAL} admin] +puts stdout [ovsec_kadm_randkey_principal $server_handle notserver/$h key] +puts stdout [ovsec_kadm_destroy $server_handle] +EOF + +rm -f $RPC_TEST_SRVTAB + +eval $MAKE_KEYTAB -princ server/$CANON_HOST $RPC_TEST_SRVTAB $REDIRECT + +grep -s "$CANON_HOST SECURE-TEST.OV.COM" /etc/krb.realms +if [ $? != 0 ]; then + eval echo \"Adding \$CANON_HOST SECURE-TEST.OV.COM to /etc/krb.realms\" $REDIRECT + ed /etc/krb.realms </dev/null +1i +$CANON_HOST SECURE-TEST.OV.COM +. +w +q +EOF +fi diff --git a/src/lib/rpc/xdr.c b/src/lib/rpc/xdr.c new file mode 100644 index 000000000..7f4418162 --- /dev/null +++ b/src/lib/rpc/xdr.c @@ -0,0 +1,674 @@ +/* @(#)xdr.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr.c 1.35 87/08/12"; +#endif + +/* + * xdr.c, Generic XDR routines implementation. + * + * Copyright (C) 1986, Sun Microsystems, Inc. + * + * These are the "generic" xdr routines used to serialize and de-serialize + * most common data items. See xdr.h for more info on the interface to + * xdr. + */ + +#include +#include +char *malloc(); + +#include +#include + +/* + * constants specific to the xdr "protocol" + */ +#define XDR_FALSE ((rpc_int32) 0) +#define XDR_TRUE ((rpc_int32) 1) +#define LASTUNSIGNED ((unsigned int) 0-1) + +/* + * for unit alignment + */ +static char xdr_zero[BYTES_PER_XDR_UNIT] = { 0, 0, 0, 0 }; + +/* + * Free a data structure using XDR + * Not a filter, but a convenient utility nonetheless + */ +void +xdr_free(proc, objp) + xdrproc_t proc; + char *objp; +{ + XDR x; + + x.x_op = XDR_FREE; + (*proc)(&x, objp); +} + +/* + * XDR nothing + */ +bool_t +xdr_void(/* xdrs, addr */) + /* XDR *xdrs; */ + /* caddr_t addr; */ +{ + + return (TRUE); +} + +/* + * XDR integers + */ +bool_t +xdr_int(xdrs, ip) + XDR *xdrs; + int *ip; +{ + +#ifdef lint + (void) (xdr_short(xdrs, (short *)ip)); + return (xdr_long(xdrs, (rpc_int32 *)ip)); +#else + if (sizeof (int) >= 4) { + long l; + switch (xdrs->x_op) { + case XDR_ENCODE: + l = *ip; + return (xdr_long(xdrs, &l)); + + case XDR_DECODE: + if (!xdr_long(xdrs, &l)) { + return (FALSE); + } + *ip = l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + } else { + return (xdr_short(xdrs, (short *)ip)); + } +#endif +} + +/* + * XDR unsigned integers + */ +bool_t +xdr_u_int(xdrs, up) + XDR *xdrs; + unsigned int *up; +{ +#ifdef lint + (void) (xdr_short(xdrs, (short *)up)); + return (xdr_u_long(xdrs, (rpc_u_int32 *)up)); +#else + if (sizeof (unsigned int) >= 4) { + unsigned long l; + switch (xdrs->x_op) { + case XDR_ENCODE: + l = *up; + return (xdr_u_long(xdrs, &l)); + + case XDR_DECODE: + if (!xdr_u_long(xdrs, &l)) { + return (FALSE); + } + *up = l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + } else { + return (xdr_short(xdrs, (short *)up)); + } +#endif +} + +/* + * XDR long integers + * same as xdr_u_long - open coded to save a proc call! + */ +bool_t +xdr_long(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + if (xdrs->x_op == XDR_ENCODE) { + if (sizeof (long) > 4) { + /* See if the dereferenced value fits in 4 bytes. If not, return FALSE. + * Check by loading value into a rpc_int32, then loading back and comparing + * results. + */ + rpc_int32 i = (int) *lp; + long l = i; + if (l != *lp) { + return (FALSE); + } + } + return (XDR_PUTLONG(xdrs, lp)); + } + if (xdrs->x_op == XDR_DECODE) + return (XDR_GETLONG(xdrs, lp)); + + if (xdrs->x_op == XDR_FREE) + return (TRUE); + + return (FALSE); +} + +/* + * XDR unsigned long integers + * same as xdr_long - open coded to save a proc call! + */ +bool_t +xdr_u_long(xdrs, ulp) + register XDR *xdrs; + unsigned long *ulp; +{ + if (xdrs->x_op == XDR_ENCODE) { + if (sizeof (unsigned long) > 4) { + /* See if the dereferenced value fits in 4 bytes. If not, return FALSE. + * Check by loading value into a rpc_int32, then loading back and comparing + * results. + */ + unsigned int ui = *ulp; + unsigned long ul = ui; + if (ul != *ulp) { + return (FALSE); + } + } + return (XDR_PUTLONG(xdrs, ulp)); + } + if (xdrs->x_op == XDR_DECODE) { + return (XDR_GETLONG(xdrs, (long *)ulp)); + } + if (xdrs->x_op == XDR_FREE) + return (TRUE); + return (FALSE); +} + +/* + * XDR short integers + */ +bool_t +xdr_short(xdrs, sp) + register XDR *xdrs; + short *sp; +{ + long l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (long) *sp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *sp = (short) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR unsigned short integers + */ +bool_t +xdr_u_short(xdrs, usp) + register XDR *xdrs; + unsigned short *usp; +{ + unsigned long l; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + l = (unsigned long) *usp; + return (XDR_PUTLONG(xdrs, &l)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &l)) { + return (FALSE); + } + *usp = (unsigned short) l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + + +/* + * XDR a char + */ +bool_t +xdr_char(xdrs, cp) + XDR *xdrs; + char *cp; +{ + int i; + + i = (*cp); + if (!xdr_int(xdrs, &i)) { + return (FALSE); + } + *cp = i; + return (TRUE); +} + +/* + * XDR an unsigned char + */ +bool_t +xdr_u_char(xdrs, cp) + XDR *xdrs; + char *cp; +{ + unsigned int u; + + u = (*cp); + if (!xdr_u_int(xdrs, &u)) { + return (FALSE); + } + *cp = u; + return (TRUE); +} + +/* + * XDR booleans + */ +bool_t +xdr_bool(xdrs, bp) + register XDR *xdrs; + bool_t *bp; +{ + long lb; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + lb = *bp ? XDR_TRUE : XDR_FALSE; + return (XDR_PUTLONG(xdrs, &lb)); + + case XDR_DECODE: + if (!XDR_GETLONG(xdrs, &lb)) { + return (FALSE); + } + *bp = (lb == XDR_FALSE) ? FALSE : TRUE; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * XDR enumerations + */ +bool_t +xdr_enum(xdrs, ep) + XDR *xdrs; + enum_t *ep; +{ +#ifndef lint + enum sizecheck { SIZEVAL }; /* used to find the size of an enum */ + + /* + * enums are treated as ints + */ + if (sizeof (enum sizecheck) == sizeof (rpc_int32)) { + return (xdr_int32(xdrs, (rpc_int32 *)ep)); + } else if (sizeof (enum sizecheck) == sizeof (short)) { + return (xdr_short(xdrs, (short *)ep)); + } else { + return (FALSE); + } +#else + (void) (xdr_short(xdrs, (short *)ep)); + return (xdr_long(xdrs, (long *)ep)); +#endif +} + +/* + * XDR opaque data + * Allows the specification of a fixed size sequence of opaque bytes. + * cp points to the opaque object and cnt gives the byte length. + */ +bool_t +xdr_opaque(xdrs, cp, cnt) + register XDR *xdrs; + caddr_t cp; + register unsigned int cnt; +{ + register unsigned int rndup; + static crud[BYTES_PER_XDR_UNIT]; + + /* + * if no data we are done + */ + if (cnt == 0) + return (TRUE); + + /* + * round byte count to full xdr units + */ + rndup = cnt % BYTES_PER_XDR_UNIT; + if (rndup > 0) + rndup = BYTES_PER_XDR_UNIT - rndup; + + if (xdrs->x_op == XDR_DECODE) { + if (!XDR_GETBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_GETBYTES(xdrs, crud, rndup)); + } + + if (xdrs->x_op == XDR_ENCODE) { + if (!XDR_PUTBYTES(xdrs, cp, cnt)) { + return (FALSE); + } + if (rndup == 0) + return (TRUE); + return (XDR_PUTBYTES(xdrs, xdr_zero, rndup)); + } + + if (xdrs->x_op == XDR_FREE) { + return (TRUE); + } + + return (FALSE); +} + +/* + * XDR counted bytes + * *cpp is a pointer to the bytes, *sizep is the count. + * If *cpp is NULL maxsize bytes are allocated + */ +bool_t +xdr_bytes(xdrs, cpp, sizep, maxsize) + register XDR *xdrs; + char **cpp; + register unsigned int *sizep; + unsigned int maxsize; +{ + register char *sp = *cpp; /* sp is the actual string pointer */ + register unsigned int nodesize; + + /* + * first deal with the length since xdr bytes are counted + */ + if (! xdr_u_int(xdrs, sizep)) { + return (FALSE); + } + nodesize = *sizep; + if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) { + return (FALSE); + } + + /* + * now deal with the actual bytes + */ + switch (xdrs->x_op) { + + case XDR_DECODE: + if (nodesize == 0) { + return (TRUE); + } + if (sp == NULL) { + *cpp = sp = (char *)mem_alloc(nodesize); + } + if (sp == NULL) { + (void) fprintf(stderr, "xdr_bytes: out of memory\n"); + return (FALSE); + } + /* fall into ... */ + + case XDR_ENCODE: + return (xdr_opaque(xdrs, sp, nodesize)); + + case XDR_FREE: + if (sp != NULL) { + mem_free(sp, nodesize); + *cpp = NULL; + } + return (TRUE); + } + return (FALSE); +} + +/* + * Implemented here due to commonality of the object. + */ +bool_t +xdr_netobj(xdrs, np) + XDR *xdrs; + struct netobj *np; +{ + + return (xdr_bytes(xdrs, &np->n_bytes, &np->n_len, MAX_NETOBJ_SZ)); +} + +bool_t +xdr_int32(xdrs, ip) + XDR *xdrs; + rpc_int32 *ip; +{ + long l; + switch (xdrs->x_op) { + case XDR_ENCODE: + l = *ip; + return (xdr_long(xdrs, &l)); + + case XDR_DECODE: + if (!xdr_long(xdrs, &l)) { + return (FALSE); + } + *ip = l; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } +} + +xdr_u_int32(xdrs, up) + XDR *xdrs; + rpc_u_int32 *up; +{ + unsigned long ul; + switch (xdrs->x_op) { + case XDR_ENCODE: + ul = *up; + return (xdr_u_long(xdrs, &ul)); + + case XDR_DECODE: + if (!xdr_u_long(xdrs, &ul)) { + return (FALSE); + } + *up = ul; + return (TRUE); + + case XDR_FREE: + return (TRUE); + } +} + +/* + * XDR a descriminated union + * Support routine for discriminated unions. + * You create an array of xdrdiscrim structures, terminated with + * an entry with a null procedure pointer. The routine gets + * the discriminant value and then searches the array of xdrdiscrims + * looking for that value. It calls the procedure given in the xdrdiscrim + * to handle the discriminant. If there is no specific routine a default + * routine may be called. + * If there is no specific or default routine an error is returned. + */ +bool_t +xdr_union(xdrs, dscmp, unp, choices, dfault) + register XDR *xdrs; + enum_t *dscmp; /* enum to decide which arm to work on */ + char *unp; /* the union itself */ + struct xdr_discrim *choices; /* [value, xdr proc] for each arm */ + xdrproc_t dfault; /* default xdr routine */ +{ + register enum_t dscm; + + /* + * we deal with the discriminator; it's an enum + */ + if (! xdr_enum(xdrs, dscmp)) { + return (FALSE); + } + dscm = *dscmp; + + /* + * search choices for a value that matches the discriminator. + * if we find one, execute the xdr routine for that value. + */ + for (; choices->proc != NULL_xdrproc_t; choices++) { + if (choices->value == dscm) + return ((*(choices->proc))(xdrs, unp, LASTUNSIGNED)); + } + + /* + * no match - execute the default xdr routine if there is one + */ + return ((dfault == NULL_xdrproc_t) ? FALSE : + (*dfault)(xdrs, unp, LASTUNSIGNED)); +} + + +/* + * Non-portable xdr primitives. + * Care should be taken when moving these routines to new architectures. + */ + + +/* + * XDR null terminated ASCII strings + * xdr_string deals with "C strings" - arrays of bytes that are + * terminated by a NULL character. The parameter cpp references a + * pointer to storage; If the pointer is null, then the necessary + * storage is allocated. The last parameter is the max allowed length + * of the string as specified by a protocol. + */ +bool_t +xdr_string(xdrs, cpp, maxsize) + register XDR *xdrs; + char **cpp; + unsigned int maxsize; +{ + register char *sp = *cpp; /* sp is the actual string pointer */ + unsigned int size; + unsigned int nodesize; + + /* + * first deal with the length since xdr strings are counted-strings + */ + switch (xdrs->x_op) { + case XDR_FREE: + if (sp == NULL) { + return(TRUE); /* already free */ + } + /* fall through... */ + case XDR_ENCODE: + size = strlen(sp); + break; + } + if (! xdr_u_int(xdrs, &size)) { + return (FALSE); + } + if (size > maxsize) { + return (FALSE); + } + nodesize = size + 1; + + /* + * now deal with the actual bytes + */ + switch (xdrs->x_op) { + + case XDR_DECODE: + if (nodesize == 0) { + return (TRUE); + } + if (sp == NULL) + *cpp = sp = (char *)mem_alloc(nodesize); + if (sp == NULL) { + (void) fprintf(stderr, "xdr_string: out of memory\n"); + return (FALSE); + } + sp[size] = 0; + /* fall into ... */ + + case XDR_ENCODE: + return (xdr_opaque(xdrs, sp, size)); + + case XDR_FREE: + mem_free(sp, nodesize); + *cpp = NULL; + return (TRUE); + } + return (FALSE); +} + +/* + * Wrapper for xdr_string that can be called directly from + * routines like clnt_call + */ +bool_t +xdr_wrapstring(xdrs, cpp) + XDR *xdrs; + char **cpp; +{ + if (xdr_string(xdrs, cpp, LASTUNSIGNED)) { + return (TRUE); + } + return (FALSE); +} diff --git a/src/lib/rpc/xdr.h b/src/lib/rpc/xdr.h new file mode 100644 index 000000000..9f0819b61 --- /dev/null +++ b/src/lib/rpc/xdr.h @@ -0,0 +1,276 @@ +/* @(#)xdr.h 2.2 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* @(#)xdr.h 1.19 87/04/22 SMI */ + +/* + * xdr.h, External Data Representation Serialization Routines. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + */ + +#ifndef __XDR_HEADER__ +#define __XDR_HEADER__ + +/* + * XDR provides a conventional way for converting between C data + * types and an external bit-string representation. Library supplied + * routines provide for the conversion on built-in C data types. These + * routines and utility routines defined here are used to help implement + * a type encode/decode routine for each user-defined type. + * + * Each data type provides a single procedure which takes two arguments: + * + * bool_t + * xdrproc(xdrs, argresp) + * XDR *xdrs; + * *argresp; + * + * xdrs is an instance of a XDR handle, to which or from which the data + * type is to be converted. argresp is a pointer to the structure to be + * converted. The XDR handle contains an operation field which indicates + * which of the operations (ENCODE, DECODE * or FREE) is to be performed. + * + * XDR_DECODE may allocate space if the pointer argresp is null. This + * data can be freed with the XDR_FREE operation. + * + * We write only one procedure per data type to make it easy + * to keep the encode and decode procedures for a data type consistent. + * In many cases the same code performs all operations on a user defined type, + * because all the hard work is done in the component type routines. + * decode as a series of calls on the nested data types. + */ + +/* + * Xdr operations. XDR_ENCODE causes the type to be encoded into the + * stream. XDR_DECODE causes the type to be extracted from the stream. + * XDR_FREE can be used to release the space allocated by an XDR_DECODE + * request. + */ +enum xdr_op { + XDR_ENCODE=0, + XDR_DECODE=1, + XDR_FREE=2 +}; + +/* + * This is the number of bytes per unit of external data. + */ +#define BYTES_PER_XDR_UNIT (4) +#define RNDUP(x) ((((x) + BYTES_PER_XDR_UNIT - 1) / BYTES_PER_XDR_UNIT) \ + * BYTES_PER_XDR_UNIT) + +/* + * A xdrproc_t exists for each data type which is to be encoded or decoded. + * + * The second argument to the xdrproc_t is a pointer to an opaque pointer. + * The opaque pointer generally points to a structure of the data type + * to be decoded. If this pointer is 0, then the type routines should + * allocate dynamic storage of the appropriate size and return it. + * bool_t (*xdrproc_t)(XDR *, caddr_t *); + */ +typedef bool_t (*xdrproc_t)(); + +/* + * The XDR handle. + * Contains operation which is being applied to the stream, + * an operations vector for the paticular implementation (e.g. see xdr_mem.c), + * and two private fields for the use of the particular impelementation. + */ +typedef struct { + enum xdr_op x_op; /* operation; fast additional param */ + struct xdr_ops { + bool_t (*x_getlong)(); /* get a long from underlying stream */ + bool_t (*x_putlong)(); /* put a long to " */ + bool_t (*x_getbytes)();/* get some bytes from " */ + bool_t (*x_putbytes)();/* put some bytes to " */ + unsigned int (*x_getpostn)();/* returns bytes off from beginning */ + bool_t (*x_setpostn)();/* lets you reposition the stream */ + rpc_int32 * (*x_inline)(); /* buf quick ptr to buffered data */ + void (*x_destroy)(); /* free privates of this xdr_stream */ + } *x_ops; + caddr_t x_public; /* users' data */ + caddr_t x_private; /* pointer to private data */ + caddr_t x_base; /* private used for position info */ + int x_handy; /* extra private word */ +} XDR; + +/* + * Operations defined on a XDR handle + * + * XDR *xdrs; + * rpc_int32 *longp; + * caddr_t addr; + * unsigned int len; + * unsigned int pos; + */ +#define XDR_GETLONG(xdrs, longp) \ + (*(xdrs)->x_ops->x_getlong)(xdrs, longp) +#define xdr_getlong(xdrs, longp) \ + (*(xdrs)->x_ops->x_getlong)(xdrs, longp) + +#define XDR_PUTLONG(xdrs, longp) \ + (*(xdrs)->x_ops->x_putlong)(xdrs, longp) +#define xdr_putlong(xdrs, longp) \ + (*(xdrs)->x_ops->x_putlong)(xdrs, longp) + +#define XDR_GETBYTES(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len) +#define xdr_getbytes(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len) + +#define XDR_PUTBYTES(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len) +#define xdr_putbytes(xdrs, addr, len) \ + (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len) + +#define XDR_GETPOS(xdrs) \ + (*(xdrs)->x_ops->x_getpostn)(xdrs) +#define xdr_getpos(xdrs) \ + (*(xdrs)->x_ops->x_getpostn)(xdrs) + +#define XDR_SETPOS(xdrs, pos) \ + (*(xdrs)->x_ops->x_setpostn)(xdrs, pos) +#define xdr_setpos(xdrs, pos) \ + (*(xdrs)->x_ops->x_setpostn)(xdrs, pos) + +#define XDR_INLINE(xdrs, len) \ + (*(xdrs)->x_ops->x_inline)(xdrs, len) +#define xdr_inline(xdrs, len) \ + (*(xdrs)->x_ops->x_inline)(xdrs, len) + +#define XDR_DESTROY(xdrs) \ + if ((xdrs)->x_ops->x_destroy) \ + (*(xdrs)->x_ops->x_destroy)(xdrs) +#define xdr_destroy(xdrs) \ + if ((xdrs)->x_ops->x_destroy) \ + (*(xdrs)->x_ops->x_destroy)(xdrs) + +/* + * Support struct for discriminated unions. + * You create an array of xdrdiscrim structures, terminated with + * a entry with a null procedure pointer. The xdr_union routine gets + * the discriminant value and then searches the array of structures + * for a matching value. If a match is found the associated xdr routine + * is called to handle that part of the union. If there is + * no match, then a default routine may be called. + * If there is no match and no default routine it is an error. + */ +#define NULL_xdrproc_t ((xdrproc_t)0) +struct xdr_discrim { + int value; + xdrproc_t proc; +}; + +/* + * In-line routines for fast encode/decode of primitve data types. + * Caveat emptor: these use single memory cycles to get the + * data from the underlying buffer, and will fail to operate + * properly if the data is not aligned. The standard way to use these + * is to say: + * if ((buf = XDR_INLINE(xdrs, count)) == NULL) + * return (FALSE); + * <<< macro calls >>> + * where ``count'' is the number of bytes of data occupied + * by the primitive data types. + * + * N.B. and frozen for all time: each data type here uses 4 bytes + * of external representation. + */ +#define IXDR_GET_LONG(buf) ((long)ntohl((rpc_u_int32)*(buf)++)) +#define IXDR_PUT_LONG(buf, v) (*(buf)++ = (rpc_int32)htonl((rpc_u_int32)v)) + +#define IXDR_GET_BOOL(buf) ((bool_t)IXDR_GET_LONG(buf)) +#define IXDR_GET_ENUM(buf, t) ((t)IXDR_GET_LONG(buf)) +#define IXDR_GET_U_LONG(buf) ((rpc_u_int32)IXDR_GET_LONG(buf)) +#define IXDR_GET_SHORT(buf) ((short)IXDR_GET_LONG(buf)) +#define IXDR_GET_U_SHORT(buf) ((unsigned short)IXDR_GET_LONG(buf)) + +#define IXDR_PUT_BOOL(buf, v) IXDR_PUT_LONG((buf), ((rpc_int32)(v))) +#define IXDR_PUT_ENUM(buf, v) IXDR_PUT_LONG((buf), ((rpc_int32)(v))) +#define IXDR_PUT_U_LONG(buf, v) IXDR_PUT_LONG((buf), ((rpc_int32)(v))) +#define IXDR_PUT_SHORT(buf, v) IXDR_PUT_LONG((buf), ((rpc_int32)(v))) +#define IXDR_PUT_U_SHORT(buf, v) IXDR_PUT_LONG((buf), ((rpc_int32)(v))) + +/* + * These are the "generic" xdr routines. + */ +extern bool_t xdr_void(); +extern bool_t xdr_int(XDR *, int *); +extern bool_t xdr_u_int(XDR *, unsigned int *); +extern bool_t xdr_long(XDR *, long *); +extern bool_t xdr_u_long(XDR *, unsigned long *); +extern bool_t xdr_short(XDR *, short *); +extern bool_t xdr_u_short(XDR *, unsigned short *); +extern bool_t xdr_bool(XDR *, bool_t *); +extern bool_t xdr_enum(XDR *, enum_t *); +extern bool_t xdr_array(XDR *, caddr_t *, unsigned int*, unsigned int, unsigned int, xdrproc_t); +extern bool_t xdr_bytes(XDR *, char **, unsigned int *, unsigned int); +extern bool_t xdr_opaque(XDR *, caddr_t, unsigned int); +extern bool_t xdr_string(XDR *, char **, unsigned int); +extern bool_t xdr_union(XDR *, enum_t *, char *, struct xdr_discrim *, xdrproc_t); +extern bool_t xdr_char(XDR *, char *); +extern bool_t xdr_u_char(XDR *, char *); +extern bool_t xdr_vector(XDR *, char *, unsigned int, unsigned int, xdrproc_t); +extern bool_t xdr_float(XDR *, float *); +extern bool_t xdr_double(XDR *, double *); +extern bool_t xdr_reference(XDR *, caddr_t *, unsigned int, xdrproc_t); +extern bool_t xdr_pointer(XDR *, char **, unsigned int, xdrproc_t); +extern bool_t xdr_wrapstring(XDR *, char **); + +/* + * Common opaque bytes objects used by many rpc protocols; + * declared here due to commonality. + */ +#define MAX_NETOBJ_SZ 1024 +struct netobj { + unsigned int n_len; + char *n_bytes; +}; +typedef struct netobj netobj; +extern bool_t xdr_netobj(XDR *, struct netobj *); + +extern bool_t xdr_int32(XDR *, rpc_int32 *); +extern bool_t xdr_u_int32(XDR *, rpc_u_int32 *); + +/* + * These are the public routines for the various implementations of + * xdr streams. + */ +extern void xdrmem_create(); /* XDR using memory buffers */ +extern void xdrstdio_create(); /* XDR using stdio library */ +extern void xdrrec_create(); /* XDR pseudo records for tcp */ +extern void xdralloc_create(); /* XDR allocating memory buffer */ +extern void xdralloc_release(); /* destroy xdralloc, save buf */ +extern bool_t xdrrec_endofrecord(); /* make end of xdr record */ +extern bool_t xdrrec_skiprecord(); /* move to beginning of next record */ +extern bool_t xdrrec_eof(); /* true if no more input */ +extern caddr_t xdralloc_getdata(); /* get buffer from xdralloc */ + +#endif /* !__XDR_HEADER__ */ diff --git a/src/lib/rpc/xdr_alloc.c b/src/lib/rpc/xdr_alloc.c new file mode 100644 index 000000000..37ae71e82 --- /dev/null +++ b/src/lib/rpc/xdr_alloc.c @@ -0,0 +1,173 @@ +/* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * $Log$ + * Revision 1.6 1996/07/22 20:41:21 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.5.4.1 1996/07/18 04:19:49 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.5.2.1 1996/06/20 23:40:30 marc + * File added to the repository on a branch + * + * Revision 1.5 1996/05/12 06:19:25 marc + * renamed lots of types: u_foo to unsigned foo, and foo32 to rpc_foo32. This is to make autoconfiscation less painful. + * + * Revision 1.4 1995/12/13 14:03:14 grier + * Longs to ints for Alpha + * + * Revision 1.3 1993/12/09 18:57:25 bjaspan + * [secure-releng/833] misc bugfixes to admin library + * + * Revision 1.3 1993/12/06 21:23:08 bjaspan + * add xdralloc_release + * + * Revision 1.2 1993/10/26 21:13:19 bjaspan + * add casts for correctness + * + * Revision 1.1 1993/10/19 03:11:39 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include +#include +#include + +static bool_t xdralloc_putlong(); +static bool_t xdralloc_putbytes(); +static unsigned int xdralloc_getpos(); +static rpc_int32 * xdralloc_inline(); +static void xdralloc_destroy(); +static bool_t xdralloc_notsup(); + +static struct xdr_ops xdralloc_ops = { + xdralloc_notsup, + xdralloc_putlong, + xdralloc_notsup, + xdralloc_putbytes, + xdralloc_getpos, + xdralloc_notsup, + xdralloc_inline, + xdralloc_destroy, +}; + +/* + * The procedure xdralloc_create initializes a stream descriptor for a + * memory buffer. + */ +void xdralloc_create(xdrs, op) + register XDR *xdrs; + enum xdr_op op; +{ + xdrs->x_op = op; + xdrs->x_ops = &xdralloc_ops; + xdrs->x_private = (caddr_t) DynCreate(sizeof(char), -4); + /* not allowed to fail */ +} + +caddr_t xdralloc_getdata(xdrs) + XDR *xdrs; +{ + return (caddr_t) DynGet((DynObject) xdrs->x_private, 0); +} + +void xdralloc_release(xdrs) + XDR *xdrs; +{ + DynRelease((DynObject) xdrs->x_private); +} + +static void xdralloc_destroy(xdrs) + XDR *xdrs; +{ + DynDestroy((DynObject) xdrs->x_private); +} + +static bool_t xdralloc_notsup() +{ + return FALSE; +} + +static bool_t xdralloc_putlong(xdrs, lp) + register XDR *xdrs; + rpc_int32 *lp; +{ + int l = htonl((rpc_u_int32) *(int *)lp); + + if (DynInsert((DynObject) xdrs->x_private, + DynSize((DynObject) xdrs->x_private), &l, + sizeof(int)) != DYN_OK) + return FALSE; + return (TRUE); +} + +static bool_t xdralloc_putbytes(xdrs, addr, len) + register XDR *xdrs; + caddr_t addr; + register unsigned int len; +{ + if (DynInsert((DynObject) xdrs->x_private, + DynSize((DynObject) xdrs->x_private), + addr, len) != DYN_OK) + return FALSE; + return TRUE; +} + +static unsigned int xdralloc_getpos(xdrs) + register XDR *xdrs; +{ + return DynSize((DynObject) xdrs->x_private); +} + + +static rpc_int32 *xdralloc_inline(xdrs, len) + register XDR *xdrs; + int len; +{ + return (rpc_int32 *) 0; +} diff --git a/src/lib/rpc/xdr_array.c b/src/lib/rpc/xdr_array.c new file mode 100644 index 000000000..09bd50abd --- /dev/null +++ b/src/lib/rpc/xdr_array.c @@ -0,0 +1,153 @@ +/* @(#)xdr_array.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_array.c 1.10 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_array.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These are the "non-trivial" xdr primitives used to serialize and de-serialize + * arrays. See xdr.h for more info on the interface to xdr. + */ + +#include + +#include +#include + +#define LASTUNSIGNED ((unsigned int)0-1) + + +/* + * XDR an array of arbitrary elements + * *addrp is a pointer to the array, *sizep is the number of elements. + * If addrp is NULL (*sizep * elsize) bytes are allocated. + * elsize is the size (in bytes) of each element, and elproc is the + * xdr procedure to call to handle each element of the array. + */ +bool_t +xdr_array(xdrs, addrp, sizep, maxsize, elsize, elproc) + register XDR *xdrs; + caddr_t *addrp; /* array pointer */ + unsigned int *sizep; /* number of elements */ + unsigned int maxsize; /* max numberof elements */ + unsigned int elsize; /* size in bytes of each element */ + xdrproc_t elproc; /* xdr routine to handle each element */ +{ + register unsigned int i; + register caddr_t target = *addrp; + register unsigned int c; /* the actual element count */ + register bool_t stat = TRUE; + register unsigned int nodesize; + + /* like strings, arrays are really counted arrays */ + if (! xdr_u_int(xdrs, sizep)) { + return (FALSE); + } + c = *sizep; + if ((c > maxsize) && (xdrs->x_op != XDR_FREE)) { + return (FALSE); + } + nodesize = c * elsize; + + /* + * if we are deserializing, we may need to allocate an array. + * We also save time by checking for a null array if we are freeing. + */ + if (target == NULL) + switch (xdrs->x_op) { + case XDR_DECODE: + if (c == 0) + return (TRUE); + *addrp = target = mem_alloc(nodesize); + if (target == NULL) { + (void) fprintf(stderr, + "xdr_array: out of memory\n"); + return (FALSE); + } + memset(target, 0, nodesize); + break; + + case XDR_FREE: + return (TRUE); + } + + /* + * now we xdr each element of array + */ + for (i = 0; (i < c) && stat; i++) { + stat = (*elproc)(xdrs, target, LASTUNSIGNED); + target += elsize; + } + + /* + * the array may need freeing + */ + if (xdrs->x_op == XDR_FREE) { + mem_free(*addrp, nodesize); + *addrp = NULL; + } + return (stat); +} + +/* + * xdr_vector(): + * + * XDR a fixed length array. Unlike variable-length arrays, + * the storage of fixed length arrays is static and unfreeable. + * > basep: base of the array + * > size: size of the array + * > elemsize: size of each element + * > xdr_elem: routine to XDR each element + */ +bool_t +xdr_vector(xdrs, basep, nelem, elemsize, xdr_elem) + register XDR *xdrs; + register char *basep; + register unsigned int nelem; + register unsigned int elemsize; + register xdrproc_t xdr_elem; +{ + register unsigned int i; + register char *elptr; + + elptr = basep; + for (i = 0; i < nelem; i++) { + if (! (*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) { + return(FALSE); + } + elptr += elemsize; + } + return(TRUE); +} + diff --git a/src/lib/rpc/xdr_float.c b/src/lib/rpc/xdr_float.c new file mode 100644 index 000000000..0c153c472 --- /dev/null +++ b/src/lib/rpc/xdr_float.c @@ -0,0 +1,293 @@ +/* @(#)xdr_float.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_float.c 1.12 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_float.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These are the "floating point" xdr routines used to (de)serialize + * most common data items. See xdr.h for more info on the interface to + * xdr. + */ + +#include + +#include +#include + +/* + * NB: Not portable. + * This routine works on Suns (Sky / 68000's) and Vaxen. + */ +#ifdef IGNORE + +#ifdef vax + +/* What IEEE single precision floating point looks like on a Vax */ +struct ieee_single { + unsigned int mantissa: 23; + unsigned int exp : 8; + unsigned int sign : 1; +}; + +/* Vax single precision floating point */ +struct vax_single { + unsigned int mantissa1 : 7; + unsigned int exp : 8; + unsigned int sign : 1; + unsigned int mantissa2 : 16; +}; + +#define VAX_SNG_BIAS 0x81 +#define IEEE_SNG_BIAS 0x7f + +static struct sgl_limits { + struct vax_single s; + struct ieee_single ieee; +} sgl_limits[2] = { + {{ 0x7f, 0xff, 0x0, 0xffff }, /* Max Vax */ + { 0x0, 0xff, 0x0 }}, /* Max IEEE */ + {{ 0x0, 0x0, 0x0, 0x0 }, /* Min Vax */ + { 0x0, 0x0, 0x0 }} /* Min IEEE */ +}; +#endif /* vax */ + +bool_t +xdr_float(xdrs, fp) + register XDR *xdrs; + register float *fp; +{ +#if defined(vax) + struct ieee_single is; + struct vax_single vs, *vsp; + struct sgl_limits *lim; + int i; +#endif + long lg; + + switch (xdrs->x_op) { + + case XDR_ENCODE: +#if !defined(vax) + lg = * (int_32 *) fp; + return (XDR_PUTLONG(xdrs, &lg)); +#else + vs = *((struct vax_single *)fp); + for (i = 0, lim = sgl_limits; + i < sizeof(sgl_limits)/sizeof(struct sgl_limits); + i++, lim++) { + if ((vs.mantissa2 == lim->s.mantissa2) && + (vs.exp == lim->s.exp) && + (vs.mantissa1 == lim->s.mantissa1)) { + is = lim->ieee; + goto shipit; + } + } + is.exp = vs.exp - VAX_SNG_BIAS + IEEE_SNG_BIAS; + is.mantissa = (vs.mantissa1 << 16) | vs.mantissa2; + shipit: + is.sign = vs.sign; + return (XDR_PUTLONG(xdrs, (rpc_int32 *)&is)); +#endif + + case XDR_DECODE: +#if !defined(vax) + if (!(XDR_GETLONG(xdrs, &lg))) { + return (FALSE); + } + *fp = (float) ((int) lg); + return (TRUE); +#else + vsp = (struct vax_single *)fp; + if (!XDR_GETLONG(xdrs, (rpc_int32 *)&is)) + return (FALSE); + for (i = 0, lim = sgl_limits; + i < sizeof(sgl_limits)/sizeof(struct sgl_limits); + i++, lim++) { + if ((is.exp == lim->ieee.exp) && + (is.mantissa == lim->ieee.mantissa)) { + *vsp = lim->s; + goto doneit; + } + } + vsp->exp = is.exp - IEEE_SNG_BIAS + VAX_SNG_BIAS; + vsp->mantissa2 = is.mantissa; + vsp->mantissa1 = (is.mantissa >> 16); + doneit: + vsp->sign = is.sign; + return (TRUE); +#endif + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +/* + * This routine works on Suns (Sky / 68000's) and Vaxen. + */ + +#ifdef vax +/* What IEEE double precision floating point looks like on a Vax */ +struct ieee_double { + unsigned int mantissa1 : 20; + unsigned int exp : 11; + unsigned int sign : 1; + unsigned int mantissa2 : 32; +}; + +/* Vax double precision floating point */ +struct vax_double { + unsigned int mantissa1 : 7; + unsigned int exp : 8; + unsigned int sign : 1; + unsigned int mantissa2 : 16; + unsigned int mantissa3 : 16; + unsigned int mantissa4 : 16; +}; + +#define VAX_DBL_BIAS 0x81 +#define IEEE_DBL_BIAS 0x3ff +#define MASK(nbits) ((1 << nbits) - 1) + +static struct dbl_limits { + struct vax_double d; + struct ieee_double ieee; +} dbl_limits[2] = { + {{ 0x7f, 0xff, 0x0, 0xffff, 0xffff, 0xffff }, /* Max Vax */ + { 0x0, 0x7ff, 0x0, 0x0 }}, /* Max IEEE */ + {{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, /* Min Vax */ + { 0x0, 0x0, 0x0, 0x0 }} /* Min IEEE */ +}; + +#endif /* vax */ + + +bool_t +xdr_double(xdrs, dp) + register XDR *xdrs; + double *dp; +{ + register rpc_int32 *lp; +#if defined(vax) + struct ieee_double id; + struct vax_double vd; + register struct dbl_limits *lim; + int i; +#endif + + switch (xdrs->x_op) { + + case XDR_ENCODE: +#if !defined(vax) + lp = (rpc_int32 *)dp; + if (sizeof(rpc_int32) == sizeof(long)) { + return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp)); + } else { + long lg1 = *lp++;; + long lg2 = *lp; + return (XDR_PUTLONG(xdrs, &lg1) && XDR_PUTLONG(xdrs, &lg2)); + } +#else + vd = *((struct vax_double *)dp); + for (i = 0, lim = dbl_limits; + i < sizeof(dbl_limits)/sizeof(struct dbl_limits); + i++, lim++) { + if ((vd.mantissa4 == lim->d.mantissa4) && + (vd.mantissa3 == lim->d.mantissa3) && + (vd.mantissa2 == lim->d.mantissa2) && + (vd.mantissa1 == lim->d.mantissa1) && + (vd.exp == lim->d.exp)) { + id = lim->ieee; + goto shipit; + } + } + id.exp = vd.exp - VAX_DBL_BIAS + IEEE_DBL_BIAS; + id.mantissa1 = (vd.mantissa1 << 13) | (vd.mantissa2 >> 3); + id.mantissa2 = ((vd.mantissa2 & MASK(3)) << 29) | + (vd.mantissa3 << 13) | + ((vd.mantissa4 >> 3) & MASK(13)); + shipit: + id.sign = vd.sign; + lp = (rpc_int32 *)&id; + return (XDR_PUTLONG(xdrs, lp++) && XDR_PUTLONG(xdrs, lp)); +#endif + + case XDR_DECODE: +#if !defined(vax) + lp = (rpc_int32 *)dp; + if (sizeof(rpc_int32) == sizeof(long)) { + return (XDR_GETLONG(xdrs, lp++) && XDR_GETLONG(xdrs, lp)); + } else { + long lg1, lg2; + bool_t flag = + (XDR_GETLONG(xdrs, &lg1) && XDR_GETLONG(xdrs, &lg2)); + *lp++ = lg1; + *lp = lg2; + return flag; + } +#else + lp = (rpc_int32 *)&id; + if (!XDR_GETLONG(xdrs, lp++) || !XDR_GETLONG(xdrs, lp)) + return (FALSE); + for (i = 0, lim = dbl_limits; + i < sizeof(dbl_limits)/sizeof(struct dbl_limits); + i++, lim++) { + if ((id.mantissa2 == lim->ieee.mantissa2) && + (id.mantissa1 == lim->ieee.mantissa1) && + (id.exp == lim->ieee.exp)) { + vd = lim->d; + goto doneit; + } + } + vd.exp = id.exp - IEEE_DBL_BIAS + VAX_DBL_BIAS; + vd.mantissa1 = (id.mantissa1 >> 13); + vd.mantissa2 = ((id.mantissa1 & MASK(13)) << 3) | + (id.mantissa2 >> 29); + vd.mantissa3 = (id.mantissa2 >> 13); + vd.mantissa4 = (id.mantissa2 << 3); + doneit: + vd.sign = id.sign; + *dp = *((double *)&vd); + return (TRUE); +#endif + + case XDR_FREE: + return (TRUE); + } + return (FALSE); +} + +#endif /* IGNORE */ diff --git a/src/lib/rpc/xdr_mem.c b/src/lib/rpc/xdr_mem.c new file mode 100644 index 000000000..39c0df203 --- /dev/null +++ b/src/lib/rpc/xdr_mem.c @@ -0,0 +1,188 @@ +/* @(#)xdr_mem.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_mem.c 1.19 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_mem.h, XDR implementation using memory buffers. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * If you have some data to be interpreted as external data representation + * or to be converted to external data representation in a memory buffer, + * then this is the package for you. + * + */ + + +#include +#include +#include +#include + +static bool_t xdrmem_getlong(); +static bool_t xdrmem_putlong(); +static bool_t xdrmem_getbytes(); +static bool_t xdrmem_putbytes(); +static unsigned int xdrmem_getpos(); +static bool_t xdrmem_setpos(); +static rpc_int32 * xdrmem_inline(); +static void xdrmem_destroy(); + +static struct xdr_ops xdrmem_ops = { + xdrmem_getlong, + xdrmem_putlong, + xdrmem_getbytes, + xdrmem_putbytes, + xdrmem_getpos, + xdrmem_setpos, + xdrmem_inline, + xdrmem_destroy +}; + +/* + * The procedure xdrmem_create initializes a stream descriptor for a + * memory buffer. + */ +void +xdrmem_create(xdrs, addr, size, op) + register XDR *xdrs; + caddr_t addr; + unsigned int size; + enum xdr_op op; +{ + + xdrs->x_op = op; + xdrs->x_ops = &xdrmem_ops; + xdrs->x_private = xdrs->x_base = addr; + xdrs->x_handy = size; +} + +static void +xdrmem_destroy(/*xdrs*/) + /*XDR *xdrs;*/ +{ +} + +static bool_t +xdrmem_getlong(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + + if ((xdrs->x_handy -= sizeof(rpc_int32)) < 0) + return (FALSE); + *lp = (long)ntohl(*((rpc_u_int32 *)(xdrs->x_private))); + xdrs->x_private += sizeof(rpc_int32); + return (TRUE); +} + +static bool_t +xdrmem_putlong(xdrs, lp) + register XDR *xdrs; + long *lp; +{ + + if ((xdrs->x_handy -= sizeof(rpc_int32)) < 0) + return (FALSE); + *(rpc_int32 *)xdrs->x_private = (rpc_int32)htonl((rpc_u_int32)(*lp)); + xdrs->x_private += sizeof(rpc_int32); + return (TRUE); +} + +static bool_t +xdrmem_getbytes(xdrs, addr, len) + register XDR *xdrs; + caddr_t addr; + register unsigned int len; +{ + + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memmove(addr, xdrs->x_private, len); + xdrs->x_private += len; + return (TRUE); +} + +static bool_t +xdrmem_putbytes(xdrs, addr, len) + register XDR *xdrs; + caddr_t addr; + register unsigned int len; +{ + + if ((xdrs->x_handy -= len) < 0) + return (FALSE); + memmove(xdrs->x_private, addr, len); + xdrs->x_private += len; + return (TRUE); +} + +static unsigned int +xdrmem_getpos(xdrs) + register XDR *xdrs; +{ +/* + * 11/3/95 - JRG - Rather than recast everything for 64 bit, just convert + * pointers to longs, then cast to int. + */ + return (unsigned int)((unsigned long)xdrs->x_private - (unsigned long)xdrs->x_base); +} + +static bool_t +xdrmem_setpos(xdrs, pos) + register XDR *xdrs; + unsigned int pos; +{ + register caddr_t newaddr = xdrs->x_base + pos; + register caddr_t lastaddr = xdrs->x_private + xdrs->x_handy; + + if ((long)newaddr > (long)lastaddr) + return (FALSE); + xdrs->x_private = newaddr; + xdrs->x_handy = (int)((long)lastaddr - (long)newaddr); + return (TRUE); +} + +static rpc_int32 * +xdrmem_inline(xdrs, len) + register XDR *xdrs; + int len; +{ + rpc_int32 *buf = 0; + + if (xdrs->x_handy >= len) { + xdrs->x_handy -= len; + buf = (rpc_int32 *) xdrs->x_private; + xdrs->x_private += len; + } + return (buf); +} diff --git a/src/lib/rpc/xdr_rec.c b/src/lib/rpc/xdr_rec.c new file mode 100644 index 000000000..99ce9235a --- /dev/null +++ b/src/lib/rpc/xdr_rec.c @@ -0,0 +1,596 @@ +/* @(#)xdr_rec.c 2.2 88/08/01 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_rec.c 1.21 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_rec.c, Implements TCP/IP based XDR streams with a "record marking" + * layer above tcp (for rpc's use). + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * These routines interface XDRSTREAMS to a tcp/ip connection. + * There is a record marking layer between the xdr stream + * and the tcp transport level. A record is composed on one or more + * record fragments. A record fragment is a thirty-two bit header followed + * by n bytes of data, where n is contained in the header. The header + * is represented as a htonl(rpc_u_int32). Thegh order bit encodes + * whether or not the fragment is the last fragment of the record + * (1 => fragment is last, 0 => more fragments to follow. + * The other 31 bits encode the byte length of the fragment. + */ + +#include +#include +#include +#include + +#include + +static unsigned int fix_buf_size(); +static bool_t flush_out(); +static bool_t get_input_bytes(); +static bool_t set_input_fragment(); +static bool_t skip_input_bytes(); + +static bool_t xdrrec_getlong(); +static bool_t xdrrec_putlong(); +static bool_t xdrrec_getbytes(); +static bool_t xdrrec_putbytes(); +static unsigned int xdrrec_getpos(); +static bool_t xdrrec_setpos(); +static rpc_int32 * xdrrec_inline(); +static void xdrrec_destroy(); + +static struct xdr_ops xdrrec_ops = { + xdrrec_getlong, + xdrrec_putlong, + xdrrec_getbytes, + xdrrec_putbytes, + xdrrec_getpos, + xdrrec_setpos, + xdrrec_inline, + xdrrec_destroy +}; + +/* + * A record is composed of one or more record fragments. + * A record fragment is a two-byte header followed by zero to + * 2**32-1 bytes. The header is treated as an unsigned 32 bit integer and is + * encode/decoded to the network via htonl/ntohl. The low order 31 bits + * are a byte count of the fragment. The highest order bit is a boolean: + * 1 => this fragment is the last fragment of the record, + * 0 => this fragment is followed by more fragment(s). + * + * The fragment/record machinery is not general; it is constructed to + * meet the needs of xdr and rpc based on tcp. + */ + +#define LAST_FRAG ((rpc_u_int32)(1 << 31)) + +typedef struct rec_strm { + caddr_t tcp_handle; + caddr_t the_buffer; + /* + * out-goung bits + */ + int (*writeit)(); + caddr_t out_base; /* output buffer (points to frag header) */ + caddr_t out_finger; /* next output position */ + caddr_t out_boundry; /* data cannot up to this address */ + rpc_u_int32 *frag_header; /* beginning of curren fragment */ + bool_t frag_sent; /* true if buffer sent in middle of record */ + /* + * in-coming bits + */ + int (*readit)(); + rpc_u_int32 in_size; /* fixed size of the input buffer */ + caddr_t in_base; + caddr_t in_finger; /* location of next byte to be had */ + caddr_t in_boundry; /* can read up to this location */ + rpc_int32 fbtbc; /* fragment bytes to be consumed */ + bool_t last_frag; + unsigned int sendsize; + unsigned int recvsize; +} RECSTREAM; + + +/* + * Create an xdr handle for xdrrec + * xdrrec_create fills in xdrs. Sendsize and recvsize are + * send and recv buffer sizes (0 => use default). + * tcp_handle is an opaque handle that is passed as the first parameter to + * the procedures readit and writeit. Readit and writeit are read and + * write respectively. They are like the system + * calls expect that they take an opaque handle rather than an fd. + */ +void +xdrrec_create(xdrs, sendsize, recvsize, tcp_handle, readit, writeit) + register XDR *xdrs; + register unsigned int sendsize; + register unsigned int recvsize; + caddr_t tcp_handle; + int (*readit)(); /* like read, but pass it a tcp_handle, not sock */ + int (*writeit)(); /* like write, but pass it a tcp_handle, not sock */ +{ + register RECSTREAM *rstrm = + (RECSTREAM *)mem_alloc(sizeof(RECSTREAM)); + + if (rstrm == NULL) { + (void)fprintf(stderr, "xdrrec_create: out of memory\n"); + /* + * This is bad. Should rework xdrrec_create to + * return a handle, and in this case return NULL + */ + return; + } + /* + * adjust sizes and allocate buffer quad byte aligned + */ + rstrm->sendsize = sendsize = fix_buf_size(sendsize); + rstrm->recvsize = recvsize = fix_buf_size(recvsize); + rstrm->the_buffer = mem_alloc(sendsize + recvsize + BYTES_PER_XDR_UNIT); + if (rstrm->the_buffer == NULL) { + (void)fprintf(stderr, "xdrrec_create: out of memory\n"); + return; + } + for (rstrm->out_base = rstrm->the_buffer; + /* Pointer arithmetic - long cast allowed... */ + (unsigned long)rstrm->out_base % BYTES_PER_XDR_UNIT != 0; + rstrm->out_base++); + rstrm->in_base = rstrm->out_base + sendsize; + /* + * now the rest ... + */ + xdrs->x_ops = &xdrrec_ops; + xdrs->x_private = (caddr_t)rstrm; + rstrm->tcp_handle = tcp_handle; + rstrm->readit = readit; + rstrm->writeit = writeit; + rstrm->out_finger = rstrm->out_boundry = rstrm->out_base; + rstrm->frag_header = (rpc_u_int32 *)rstrm->out_base; + rstrm->out_finger += sizeof(rpc_u_int32); + rstrm->out_boundry += sendsize; + rstrm->frag_sent = FALSE; + rstrm->in_size = recvsize; + rstrm->in_boundry = rstrm->in_base; + rstrm->in_finger = (rstrm->in_boundry += recvsize); + rstrm->fbtbc = 0; + rstrm->last_frag = TRUE; +} + + +/* + * The reoutines defined below are the xdr ops which will go into the + * xdr handle filled in by xdrrec_create. + */ + +static bool_t +xdrrec_getlong(xdrs, lp) + XDR *xdrs; + long *lp; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + register rpc_int32 *buflp = (rpc_int32 *)(rstrm->in_finger); + int mylong; + + /* first try the inline, fast case */ + if ((rstrm->fbtbc >= sizeof(rpc_int32)) && + (((long)rstrm->in_boundry - (long)buflp) >= sizeof(rpc_int32))) { + *lp = (long)ntohl((rpc_u_int32)(*buflp)); + rstrm->fbtbc -= sizeof(rpc_int32); + rstrm->in_finger += sizeof(rpc_int32); + } else { + if (! xdrrec_getbytes(xdrs, (caddr_t)&mylong, sizeof(rpc_int32))) + return (FALSE); + *lp = (long)ntohl((unsigned int)mylong); + } + return (TRUE); +} + +static bool_t +xdrrec_putlong(xdrs, lp) + XDR *xdrs; + long *lp; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + register rpc_int32 *dest_lp = ((rpc_int32 *)(rstrm->out_finger)); + + if ((rstrm->out_finger += sizeof(rpc_int32)) > rstrm->out_boundry) { + /* + * this case should almost never happen so the code is + * inefficient + */ + rstrm->out_finger -= sizeof(rpc_int32); + rstrm->frag_sent = TRUE; + if (! flush_out(rstrm, FALSE)) + return (FALSE); + dest_lp = ((rpc_int32 *)(rstrm->out_finger)); + rstrm->out_finger += sizeof(rpc_int32); + } + *dest_lp = (rpc_int32)htonl((rpc_u_int32)(*lp)); + return (TRUE); +} + +static bool_t /* must manage buffers, fragments, and records */ +xdrrec_getbytes(xdrs, addr, len) + XDR *xdrs; + register caddr_t addr; + register unsigned int len; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + register int current; + + while (len > 0) { + current = rstrm->fbtbc; + if (current == 0) { + if (rstrm->last_frag) + return (FALSE); + if (! set_input_fragment(rstrm)) + return (FALSE); + continue; + } + current = (len < current) ? len : current; + if (! get_input_bytes(rstrm, addr, current)) + return (FALSE); + addr += current; + rstrm->fbtbc -= current; + len -= current; + } + return (TRUE); +} + +static bool_t +xdrrec_putbytes(xdrs, addr, len) + XDR *xdrs; + register caddr_t addr; + register unsigned int len; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + register int current; + + while (len > 0) { + current = (int) ((long)rstrm->out_boundry - + (long)rstrm->out_finger); + current = (len < current) ? len : current; + memmove(rstrm->out_finger, addr, current); + rstrm->out_finger += current; + addr += current; + len -= current; + if (rstrm->out_finger == rstrm->out_boundry) { + rstrm->frag_sent = TRUE; + if (! flush_out(rstrm, FALSE)) + return (FALSE); + } + } + return (TRUE); +} + +static unsigned int +xdrrec_getpos(xdrs) + register XDR *xdrs; +{ + register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + register int pos; + +/* 11/5/95 JRG HELP! lseek() can't take a pointer as the first arg + * This code must have always failed, and the failure let the arithmetic + * calculations proceed + */ +#ifdef __osf__ + pos = -1; +#else + pos = lseek((int)rstrm->tcp_handle, (long) 0, 1); +#endif + if (pos != -1) + switch (xdrs->x_op) { + + case XDR_ENCODE: + pos += rstrm->out_finger - rstrm->out_base; + break; + + case XDR_DECODE: + pos -= rstrm->in_boundry - rstrm->in_finger; + break; + + default: + pos = (unsigned int) -1; + break; + } + return ((unsigned int) pos); +} + +static bool_t +xdrrec_setpos(xdrs, pos) + register XDR *xdrs; + unsigned int pos; +{ + register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + unsigned int currpos = xdrrec_getpos(xdrs); + int delta = currpos - pos; + caddr_t newpos; + + if ((int)currpos != -1) + switch (xdrs->x_op) { + + case XDR_ENCODE: + newpos = rstrm->out_finger - delta; + if ((newpos > (caddr_t)(rstrm->frag_header)) && + (newpos < rstrm->out_boundry)) { + rstrm->out_finger = newpos; + return (TRUE); + } + break; + + case XDR_DECODE: + newpos = rstrm->in_finger - delta; + if ((delta < (int)(rstrm->fbtbc)) && + (newpos <= rstrm->in_boundry) && + (newpos >= rstrm->in_base)) { + rstrm->in_finger = newpos; + rstrm->fbtbc -= delta; + return (TRUE); + } + break; + } + return (FALSE); +} + +static rpc_int32 * +xdrrec_inline(xdrs, len) + register XDR *xdrs; + int len; +{ + register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + rpc_int32 * buf = NULL; + + switch (xdrs->x_op) { + + case XDR_ENCODE: + if ((rstrm->out_finger + len) <= rstrm->out_boundry) { + buf = (rpc_int32 *) rstrm->out_finger; + rstrm->out_finger += len; + } + break; + + case XDR_DECODE: + if ((len <= rstrm->fbtbc) && + ((rstrm->in_finger + len) <= rstrm->in_boundry)) { + buf = (rpc_int32 *) rstrm->in_finger; + rstrm->fbtbc -= len; + rstrm->in_finger += len; + } + break; + } + return (buf); +} + +static void +xdrrec_destroy(xdrs) + register XDR *xdrs; +{ + register RECSTREAM *rstrm = (RECSTREAM *)xdrs->x_private; + + mem_free(rstrm->the_buffer, + rstrm->sendsize + rstrm->recvsize + BYTES_PER_XDR_UNIT); + mem_free((caddr_t)rstrm, sizeof(RECSTREAM)); +} + + +/* + * Exported routines to manage xdr records + */ + +/* + * Before reading (deserializing from the stream, one should always call + * this procedure to guarantee proper record alignment. + */ +bool_t +xdrrec_skiprecord(xdrs) + XDR *xdrs; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + + while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { + if (! skip_input_bytes(rstrm, rstrm->fbtbc)) + return (FALSE); + rstrm->fbtbc = 0; + if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) + return (FALSE); + } + rstrm->last_frag = FALSE; + return (TRUE); +} + +/* + * Look ahead fuction. + * Returns TRUE iff there is no more input in the buffer + * after consuming the rest of the current record. + */ +bool_t +xdrrec_eof(xdrs) + XDR *xdrs; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + + while (rstrm->fbtbc > 0 || (! rstrm->last_frag)) { + if (! skip_input_bytes(rstrm, rstrm->fbtbc)) + return (TRUE); + rstrm->fbtbc = 0; + if ((! rstrm->last_frag) && (! set_input_fragment(rstrm))) + return (TRUE); + } + if (rstrm->in_finger == rstrm->in_boundry) + return (TRUE); + return (FALSE); +} + +/* + * The client must tell the package when an end-of-record has occurred. + * The second paraemters tells whether the record should be flushed to the + * (output) tcp stream. (This let's the package support batched or + * pipelined procedure calls.) TRUE => immmediate flush to tcp connection. + */ +bool_t +xdrrec_endofrecord(xdrs, sendnow) + XDR *xdrs; + bool_t sendnow; +{ + register RECSTREAM *rstrm = (RECSTREAM *)(xdrs->x_private); + register rpc_u_int32 len; /* fragment length */ + + if (sendnow || rstrm->frag_sent || + ((long)rstrm->out_finger + sizeof(unsigned int) >= + (long)rstrm->out_boundry)) { + rstrm->frag_sent = FALSE; + return (flush_out(rstrm, TRUE)); + } + len = (long)(rstrm->out_finger) - (long)(rstrm->frag_header) - + sizeof(unsigned int); + *(rstrm->frag_header) = htonl((unsigned int)len | LAST_FRAG); + rstrm->frag_header = (rpc_u_int32 *)rstrm->out_finger; + rstrm->out_finger += sizeof(unsigned int); + return (TRUE); +} + + +/* + * Internal useful routines + */ +static bool_t +flush_out(rstrm, eor) + register RECSTREAM *rstrm; + bool_t eor; +{ + register rpc_u_int32 eormask = (eor == TRUE) ? LAST_FRAG : 0; + register rpc_u_int32 len = (unsigned long)(rstrm->out_finger) - + (unsigned long)(rstrm->frag_header) - sizeof(rpc_u_int32); + + *(rstrm->frag_header) = htonl(len | eormask); + len = (unsigned long)(rstrm->out_finger) - (unsigned long)(rstrm->out_base); + if ((*(rstrm->writeit))(rstrm->tcp_handle, rstrm->out_base, (int)len) + != (int)len) + return (FALSE); + rstrm->frag_header = (rpc_u_int32 *)rstrm->out_base; + rstrm->out_finger = (caddr_t)rstrm->out_base + sizeof(rpc_u_int32); + return (TRUE); +} + +static bool_t /* knows nothing about records! Only about input buffers */ +fill_input_buf(rstrm) + register RECSTREAM *rstrm; +{ + register caddr_t where; + unsigned int i; + register int len; + + where = rstrm->in_base; + i = (unsigned int)((unsigned long)rstrm->in_boundry % BYTES_PER_XDR_UNIT); + where += i; + len = rstrm->in_size - i; + if ((len = (*(rstrm->readit))(rstrm->tcp_handle, where, len)) == -1) + return (FALSE); + rstrm->in_finger = where; + where += len; + rstrm->in_boundry = where; + return (TRUE); +} + +static bool_t /* knows nothing about records! Only about input buffers */ +get_input_bytes(rstrm, addr, len) + register RECSTREAM *rstrm; + register caddr_t addr; + register int len; +{ + register int current; + + while (len > 0) { + current = (int)((long)rstrm->in_boundry - + (long)rstrm->in_finger); + if (current == 0) { + if (! fill_input_buf(rstrm)) + return (FALSE); + continue; + } + current = (len < current) ? len : current; + memmove(addr, rstrm->in_finger, current); + rstrm->in_finger += current; + addr += current; + len -= current; + } + return (TRUE); +} + +static bool_t /* next two bytes of the input stream are treated as a header */ +set_input_fragment(rstrm) + register RECSTREAM *rstrm; +{ + rpc_u_int32 header; + + if (! get_input_bytes(rstrm, (caddr_t)&header, sizeof(header))) + return (FALSE); + header = (int)ntohl(header); + rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE; + rstrm->fbtbc = header & (~LAST_FRAG); + return (TRUE); +} + +static bool_t /* consumes input bytes; knows nothing about records! */ +skip_input_bytes(rstrm, cnt) + register RECSTREAM *rstrm; + rpc_int32 cnt; +{ + register int current; + + while (cnt > 0) { + current = (int)((long)rstrm->in_boundry - + (long)rstrm->in_finger); + if (current == 0) { + if (! fill_input_buf(rstrm)) + return (FALSE); + continue; + } + current = (cnt < current) ? cnt : current; + rstrm->in_finger += current; + cnt -= current; + } + return (TRUE); +} + +static unsigned int +fix_buf_size(s) + register unsigned int s; +{ + + if (s < 100) + s = 4000; + return (RNDUP(s)); +} diff --git a/src/lib/rpc/xdr_reference.c b/src/lib/rpc/xdr_reference.c new file mode 100644 index 000000000..ebf14b68e --- /dev/null +++ b/src/lib/rpc/xdr_reference.c @@ -0,0 +1,132 @@ +/* @(#)xdr_reference.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_reference.c 1.11 87/08/11 SMI"; +#endif + +/* + * xdr_reference.c, Generic XDR routines impelmentation. + * + * Copyright (C) 1987, Sun Microsystems, Inc. + * + * These are the "non-trivial" xdr primitives used to serialize and de-serialize + * "pointers". See xdr.h for more info on the interface to xdr. + */ + +#include +#include +#include + +#define LASTUNSIGNED ((unsigned int)0-1) + +/* + * XDR an indirect pointer + * xdr_reference is for recursively translating a structure that is + * referenced by a pointer inside the structure that is currently being + * translated. pp references a pointer to storage. If *pp is null + * the necessary storage is allocated. + * size is the sizeof the referneced structure. + * proc is the routine to handle the referenced structure. + */ +bool_t +xdr_reference(xdrs, pp, size, proc) + register XDR *xdrs; + caddr_t *pp; /* the pointer to work on */ + unsigned int size; /* size of the object pointed to */ + xdrproc_t proc; /* xdr routine to handle the object */ +{ + register caddr_t loc = *pp; + register bool_t stat; + + if (loc == NULL) + switch (xdrs->x_op) { + case XDR_FREE: + return (TRUE); + + case XDR_DECODE: + *pp = loc = (caddr_t) mem_alloc(size); + if (loc == NULL) { + (void) fprintf(stderr, + "xdr_reference: out of memory\n"); + return (FALSE); + } + memset(loc, 0, (int)size); + break; + } + + stat = (*proc)(xdrs, loc, LASTUNSIGNED); + + if (xdrs->x_op == XDR_FREE) { + mem_free(loc, size); + *pp = NULL; + } + return (stat); +} + + +/* + * xdr_pointer(): + * + * XDR a pointer to a possibly recursive data structure. This + * differs with xdr_reference in that it can serialize/deserialiaze + * trees correctly. + * + * What's sent is actually a union: + * + * union object_pointer switch (boolean b) { + * case TRUE: object_data data; + * case FALSE: void nothing; + * } + * + * > objpp: Pointer to the pointer to the object. + * > obj_size: size of the object. + * > xdr_obj: routine to XDR an object. + * + */ +bool_t +xdr_pointer(xdrs,objpp,obj_size,xdr_obj) + register XDR *xdrs; + char **objpp; + unsigned int obj_size; + xdrproc_t xdr_obj; +{ + + bool_t more_data; + + more_data = (*objpp != NULL); + if (! xdr_bool(xdrs,&more_data)) { + return (FALSE); + } + if (! more_data) { + *objpp = NULL; + return (TRUE); + } + return (xdr_reference(xdrs,objpp,obj_size,xdr_obj)); +} diff --git a/src/lib/rpc/xdr_stdio.c b/src/lib/rpc/xdr_stdio.c new file mode 100644 index 000000000..6d59ad5c6 --- /dev/null +++ b/src/lib/rpc/xdr_stdio.c @@ -0,0 +1,189 @@ +/* @(#)xdr_stdio.c 2.1 88/07/29 4.0 RPCSRC */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#if !defined(lint) && defined(SCCSIDS) +static char sccsid[] = "@(#)xdr_stdio.c 1.16 87/08/11 Copyr 1984 Sun Micro"; +#endif + +/* + * xdr_stdio.c, XDR implementation on standard i/o file. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * This set of routines implements a XDR on a stdio stream. + * XDR_ENCODE serializes onto the stream, XDR_DECODE de-serializes + * from the stream. + */ + +#include +#include +#include + +static bool_t xdrstdio_getlong(); +static bool_t xdrstdio_putlong(); +static bool_t xdrstdio_getbytes(); +static bool_t xdrstdio_putbytes(); +static unsigned int xdrstdio_getpos(); +static bool_t xdrstdio_setpos(); +static rpc_int32 * xdrstdio_inline(); +static void xdrstdio_destroy(); + +/* + * Ops vector for stdio type XDR + */ +static struct xdr_ops xdrstdio_ops = { + xdrstdio_getlong, /* deseraialize a long int */ + xdrstdio_putlong, /* seraialize a long int */ + xdrstdio_getbytes, /* deserialize counted bytes */ + xdrstdio_putbytes, /* serialize counted bytes */ + xdrstdio_getpos, /* get offset in the stream */ + xdrstdio_setpos, /* set offset in the stream */ + xdrstdio_inline, /* prime stream for inline macros */ + xdrstdio_destroy /* destroy stream */ +}; + +/* + * Initialize a stdio xdr stream. + * Sets the xdr stream handle xdrs for use on the stream file. + * Operation flag is set to op. + */ +void +xdrstdio_create(xdrs, file, op) + register XDR *xdrs; + FILE *file; + enum xdr_op op; +{ + + xdrs->x_op = op; + xdrs->x_ops = &xdrstdio_ops; + xdrs->x_private = (caddr_t)file; + xdrs->x_handy = 0; + xdrs->x_base = 0; +} + +/* + * Destroy a stdio xdr stream. + * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create. + */ +static void +xdrstdio_destroy(xdrs) + register XDR *xdrs; +{ + (void)fflush((FILE *)xdrs->x_private); + /* xx should we close the file ?? */ +} + +static bool_t +xdrstdio_getlong(xdrs, lp) + XDR *xdrs; + register long *lp; +{ + rpc_int32 tmp; + if (fread((caddr_t)&tmp, + sizeof(rpc_int32), 1, (FILE *)xdrs->x_private) != 1) + return (FALSE); +#ifndef mc68000 + *lp = ntohl(tmp); +#endif + return (TRUE); +} + +static bool_t +xdrstdio_putlong(xdrs, lp) + XDR *xdrs; + long *lp; +{ + +#ifndef mc68000 + rpc_int32 mycopy = htonl((rpc_int32)*lp); +#endif + if (fwrite((caddr_t)&mycopy, sizeof(rpc_int32), 1, (FILE *)xdrs->x_private) != 1) + return (FALSE); + return (TRUE); +} + +static bool_t +xdrstdio_getbytes(xdrs, addr, len) + XDR *xdrs; + caddr_t addr; + unsigned int len; +{ + + if ((len != 0) && (fread(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1)) + return (FALSE); + return (TRUE); +} + +static bool_t +xdrstdio_putbytes(xdrs, addr, len) + XDR *xdrs; + caddr_t addr; + unsigned int len; +{ + + if ((len != 0) && (fwrite(addr, (int)len, 1, (FILE *)xdrs->x_private) != 1)) + return (FALSE); + return (TRUE); +} + +static unsigned int +xdrstdio_getpos(xdrs) + XDR *xdrs; +{ + + return ((unsigned int) ftell((FILE *)xdrs->x_private)); +} + +static bool_t +xdrstdio_setpos(xdrs, pos) + XDR *xdrs; + unsigned int pos; +{ + + return ((fseek((FILE *)xdrs->x_private, (long)pos, 0) < 0) ? + FALSE : TRUE); +} + +static rpc_int32 * +xdrstdio_inline(xdrs, len) + XDR *xdrs; + unsigned int len; +{ + + /* + * Must do some work to implement this: must insure + * enough data in the underlying stdio buffer, + * that the buffer is aligned so that we can indirect through a + * long *, and stuff this pointer in xdrs->x_buf. Doing + * a fread or fwrite to a scratch buffer would defeat + * most of the gains to be had here and require storage + * management on this buffer, so we don't do this. + */ + return (NULL); +} diff --git a/src/tests/ChangeLog b/src/tests/ChangeLog index ff23e2c68..aa7ccac08 100644 --- a/src/tests/ChangeLog +++ b/src/tests/ChangeLog @@ -1,3 +1,11 @@ +Fri Jul 19 15:31:22 1996 Marc Horowitz + + * Makefile.in (CFLAGS): the tests in Makefile.in have been + superseded by other tests in the new admin system. + + * configure.in: don't build create, since it doesn't work with the + new admin system, and isn't used by anything, anyway. + Mon Mar 18 21:49:39 1996 Ezra Peisach * configure.in: Add KRB5_RUN_FLAGS diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in index 5632988d9..6fc7ec501 100644 --- a/src/tests/Makefile.in +++ b/src/tests/Makefile.in @@ -11,7 +11,7 @@ TEST_PREFIX = "foo bar" KADMIN_OPTS= -d $(TEST_DB) -r $(TEST_REALM) -P $(TEST_MKEY) KTEST_OPTS= $(KADMIN_OPTS) -p $(TEST_PREFIX) -n $(TEST_NUM) -D $(TEST_DEPTH) -check-unix:: kdb_check +old-check-unix:: kdb_check kdb_check: $(RM) $(TEST_DB)* diff --git a/src/tests/configure.in b/src/tests/configure.in index 258b17141..9ab7794c6 100644 --- a/src/tests/configure.in +++ b/src/tests/configure.in @@ -1,6 +1,6 @@ AC_INIT(configure.in) CONFIG_RULES KRB5_RUN_FLAGS -CONFIG_DIRS(resolve asn.1 create hammer verify gssapi dejagnu) +CONFIG_DIRS(resolve asn.1 hammer verify gssapi dejagnu) DO_SUBDIRS V5_AC_OUTPUT_MAKEFILE diff --git a/src/tests/dejagnu/config/ChangeLog b/src/tests/dejagnu/config/ChangeLog index 62e2e1312..6f4019bea 100644 --- a/src/tests/dejagnu/config/ChangeLog +++ b/src/tests/dejagnu/config/ChangeLog @@ -1,3 +1,9 @@ +Fri Jul 19 19:50:23 1996 Marc Horowitz + + * default.exp: changes to work with the new admin system. This is + primarily creating the correct keytab for the new admin server, + and using the new admin client for principal creation. + Mon May 6 11:54:20 1996 Ezra Peisach * default.exp: Add procedure setup_wrapper to first setup shared diff --git a/src/tests/dejagnu/config/default.exp b/src/tests/dejagnu/config/default.exp index c5102b606..23c26361b 100644 --- a/src/tests/dejagnu/config/default.exp +++ b/src/tests/dejagnu/config/default.exp @@ -93,11 +93,11 @@ if ![info exists KRB5KDC] { } if ![info exists KADMIND] { - set KADMIND [findfile $objdir/../../kadmin/v5server/kadmind5] + set KADMIND [findfile $objdir/../../kadmin/server/kadmind] } if ![info exists KADMIN] { - set KADMIN [findfile $objdir/../../kadmin/v5client/kadmin5] + set KADMIN [findfile $objdir/../../kadmin/cli/kadmin] } if ![info exists KINIT] { @@ -334,16 +334,20 @@ proc setup_kerberos_files { } { puts $conffile "\[realms\]" puts $conffile " $REALMNAME = \{" puts $conffile " database_name = $tmppwd/db" - puts $conffile " master_key_name = master/key" - puts $conffile " master_key_type = des-cbc-md5" - puts $conffile " kdc_ports = 3088" - puts $conffile " kadmind_port = 3750" + puts $conffile " admin_database_name = $tmppwd/adb" + puts $conffile " admin_database_lockfile = $tmppwd/adb.lock" + puts $conffile " admin_keytab = $tmppwd/admin-keytab" puts $conffile " key_stash_file = $tmppwd/stash" + puts $conffile " acl_file = $tmppwd/acl" + puts $conffile " kadmind_port = 3750" puts $conffile " max_life = 1:00:00" puts $conffile " max_renewable_life = 3:00:00" + puts $conffile " master_key_type = des-cbc-md5" + puts $conffile " master_key_name = master/key" + puts $conffile " supported_enctypes = des-cbc-crc:normal des-cbc-md5:normal des-cbc-crc:v4 des-cbc-md5:norealm" + puts $conffile " kdc_ports = 3088" puts $conffile " default_principal_expiration = 99.12.31.23.59.59" puts $conffile " default_principal_flags = -postdateable forwardable" - puts $conffile " supported_enctypes = des-cbc-crc:normal des-cbc-md5:normal des-cbc-crc:v4 des-cbc-md5:norealm des3-cbc-md5:normal" puts $conffile " \}" puts $conffile "" close $conffile @@ -502,46 +506,58 @@ proc setup_kadmind_srvtab { } { global KEY global tmppwd - catch "exec rm -f tmpdir/cpw_srvtab" + catch "exec rm -f tmpdir/admin-keytab" spawn $KDB5_EDIT -r $REALMNAME expect_after { timeout { - fail "kdb5_edit cpw_srvtab" - catch "exec rm -f tmpdir/cpw_srvtab" + fail "kdb5_edit admin-keytab (timeout)" + catch "exec rm -f tmpdir/admin-keytab" catch "expect_after" return 0 } eof { - fail "kdb5_edit cpw_srvtab" - catch "exec rm -f tmpdir/cpw_srvtab" + fail "kdb5_edit admin-keytab (eof)" + catch "exec rm -f tmpdir/admin-keytab" catch "expect_after" return 0 } } expect "kdb5_edit:" - send "xst $REALMNAME changepw\r" - expect "'changepw/$REALMNAME@$REALMNAME' added to keytab" + send "xst admin kadmin\r" + expect "'kadmin/admin@$REALMNAME' added to keytab" + expect "kdb5_edit:" + + catch "exec mv -f admin-new-srvtab changepw-new-srvtab" exec_output + if ![string match "" $exec_output] { + send_log "$exec_output\n" + verbose $exec_output + send_error "ERROR: can't mv admin-new-srvtab\n" + return 0 + } + + send "xst changepw kadmin\r" + expect "'kadmin/changepw@$REALMNAME' added to keytab" expect "kdb5_edit:" send "quit\r" expect "\r" expect_after - if ![check_exit_status "kdb5_edit cpw_srvtab"] { - catch "exec rm -f tmpdir/cpw_srvtab" - send_error "ERROR: kdb5_edit cpw_srvtab exited abnormally\n" + if ![check_exit_status "kdb5_edit admin-keytab"] { + catch "exec rm -f tmpdir/admin-keytab" + send_error "ERROR: kdb5_edit admin-keytab exited abnormally\n" return 0 } - catch "exec mv -f $REALMNAME-new-srvtab tmpdir/cpw_srvtab" exec_output + catch "exec mv -f changepw-new-srvtab tmpdir/admin-keytab" exec_output if ![string match "" $exec_output] { send_log "$exec_output\n" verbose $exec_output - send_error "ERROR: can't mv new cpw_srvtab\n" + send_error "ERROR: can't mv new admin-keytab\n" return 0 } # Make the srvtab file globally readable in case we are using a # root shell and the srvtab is NFS mounted. - catch "exec chmod a+r tmpdir/cpw_srvtab" + catch "exec chmod a+r tmpdir/admin-keytab" return 1 } @@ -686,7 +702,9 @@ proc setup_kerberos_db { standalone } { expect "Re-enter password for verification:" send "adminpass$KEY\r" expect "kdb5_edit:" - send "ark changepw/$REALMNAME@$REALMNAME\r" + send "ark kadmin/admin@$REALMNAME\r" + expect "kdb5_edit:" + send "ark kadmin/changepw@$REALMNAME\r" expect "kdb5_edit:" send "quit\r" expect "\r" @@ -702,6 +720,9 @@ proc setup_kerberos_db { standalone } { return 0 } + # create the admin database lock file + catch "exec touch tmpdir/adb.lock" + if {$standalone} { pass "kdb5_edit" } @@ -797,7 +818,7 @@ proc start_kerberos_daemons { standalone } { # Start up the kadmind daemon # XXXX kadmind uses stderr a lot. the sh -c and redirect can be # removed when this is fixed - spawn $BINSH -c "exec $KADMIND -a $tmppwd/acl -r $REALMNAME -n 2>>$kadmind_lfile" + spawn $BINSH -c "exec $KADMIND -r $REALMNAME -nofork 2>>$kadmind_lfile" set kadmind_pid [exp_pid] set kadmind_spawn_id $spawn_id @@ -825,7 +846,7 @@ proc start_kerberos_daemons { standalone } { stop_kerberos_daemons return 0 } - "administrative server starting" { } + "starting" { } default { fail "kadmind (startup)" stop_kerberos_daemons @@ -886,7 +907,7 @@ proc add_kerberos_key { kkey standalone } { global spawn_id # Use kadmin to add an key. - spawn $KADMIN -m -p krbtest/admin@$REALMNAME + spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank $kkey@$REALMNAME" expect_after { "Cannot contact any KDC" { fail "kadmin interactive add $kkey lost KDC" @@ -904,23 +925,18 @@ proc add_kerberos_key { kkey standalone } { return 0 } } - expect "kadmin5:" - send "ank $kkey@$REALMNAME\r" - expect "Enter password for krbtest/admin@$REALMNAME:" + expect "Enter password:" send "adminpass$KEY\r" - expect "Enter new password for $kkey@$REALMNAME :" + expect "Enter password for principal \"$kkey@$REALMNAME\":" send "$kkey" send "$KEY\r" - expect "Re-enter new password for $kkey@$REALMNAME :" + expect "Re-enter password for principal \"$kkey@$REALMNAME\":" send "$kkey" send "$KEY\r" expect { - "principal $kkey@$REALMNAME added" { } - "Principal $kkey@$REALMNAME already exists" { } + "Principal \"$kkey@$REALMNAME\" created" { } + "Principal or policy already exists while creating" { } } - expect "kadmin5:" - send "q\r" - expect "\r" expect_after if ![check_exit_status kadmin] { return 0 @@ -946,7 +962,7 @@ proc add_random_key { kkey standalone } { global spawn_id # Use kadmin to add an key. - spawn $KADMIN -m -p krbtest/admin@$REALMNAME + spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank -randkey $kkey@$REALMNAME" expect_after { timeout { fail "kadmin $kkey" @@ -959,17 +975,12 @@ proc add_random_key { kkey standalone } { return 0 } } - expect "kadmin5:" - send "ark $kkey@$REALMNAME\r" - expect "Enter password for krbtest/admin@$REALMNAME:" + expect "Enter password:" send "adminpass$KEY\r" expect { - "principal $kkey@$REALMNAME added" { } - "Principal $kkey@$REALMNAME already exists" { } + "Principal \"$kkey@$REALMNAME\" created" { } + "Principal or policy already exists while creating" { } } - expect "kadmin5:" - send "q\r" - expect "\r" expect_after if ![check_exit_status kadmin] { return 0 diff --git a/src/tests/dejagnu/krb-standalone/ChangeLog b/src/tests/dejagnu/krb-standalone/ChangeLog index e15c07203..f1ef85e1a 100644 --- a/src/tests/dejagnu/krb-standalone/ChangeLog +++ b/src/tests/dejagnu/krb-standalone/ChangeLog @@ -1,3 +1,13 @@ +Mon Jul 22 04:19:46 1996 Marc Horowitz + + * gssftp.exp (ftp_test): check for the banner with -nocase, since + hostnames are case insensitive. + +Fri Jul 19 19:56:26 1996 Marc Horowitz + + * gssapi.exp: port to changes in gss-sample, most importantly, + output format changes and the removal of the -v2 flag. + Mon May 6 08:05:33 1996 Ezra Peisach * rcp.exp: Use a wrapper script to set up the remote rcp as diff --git a/src/tests/dejagnu/krb-standalone/gssapi.exp b/src/tests/dejagnu/krb-standalone/gssapi.exp index 06790e97a..58a9e1253 100644 --- a/src/tests/dejagnu/krb-standalone/gssapi.exp +++ b/src/tests/dejagnu/krb-standalone/gssapi.exp @@ -252,7 +252,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest0@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest0@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest0\"" catch "expect_after" if ![check_exit_status gssclient0] { @@ -293,7 +293,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest1@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest1@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest1\"" catch "expect_after" if ![check_exit_status gssclient1] { @@ -334,7 +334,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest2@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest2@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest2\"" catch "expect_after" if ![check_exit_status gssclient2] { @@ -360,7 +360,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest3@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest3@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest3\"" catch "expect_after" expect_after { @@ -388,7 +388,7 @@ proc doit { } { # Try some V2 services. # Now start the gss-server. - spawn $GSSSERVER -port 5557 -v2 gssservice@$hostname + spawn $GSSSERVER -port 5557 gssservice@$hostname set gss_server_pid [exp_pid] set gss_server_spawn_id $spawn_id catch "exec sleep 4" @@ -396,7 +396,7 @@ proc doit { } { # Start the client with client identity 0 set env(KRB5CCNAME) $tmppwd/gss_tk_0 verbose "KRB5CCNAME=$env(KRB5CCNAME)" - spawn $GSSCLIENT -port 5557 -v2 $hostname gssservice@$hostname "message from gsstest0" + spawn $GSSCLIENT -port 5557 $hostname gssservice@$hostname "message from gsstest0" expect_after { -i $spawn_id timeout { @@ -425,7 +425,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest0@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest0@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest0\"" catch "expect_after" if ![check_exit_status gssclient0] { @@ -437,7 +437,7 @@ proc doit { } { # Start the client with client identity 1 set env(KRB5CCNAME) $tmppwd/gss_tk_1 verbose "KRB5CCNAME=$env(KRB5CCNAME)" - spawn $GSSCLIENT -port 5557 -v2 $hostname gssservice@$hostname "message from gsstest1" + spawn $GSSCLIENT -port 5557 $hostname gssservice@$hostname "message from gsstest1" expect_after { -i $spawn_id timeout { @@ -466,7 +466,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest1@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest1@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest1\"" catch "expect_after" if ![check_exit_status gssclient1] { @@ -478,7 +478,7 @@ proc doit { } { # Start the client with client identity 2 set env(KRB5CCNAME) $tmppwd/gss_tk_2 verbose "KRB5CCNAME=$env(KRB5CCNAME)" - spawn $GSSCLIENT -port 5557 -v2 $hostname gssservice@$hostname "message from gsstest2" + spawn $GSSCLIENT -port 5557 $hostname gssservice@$hostname "message from gsstest2" expect_after { -i $spawn_id timeout { @@ -507,7 +507,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest2@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest2@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest2\"" catch "expect_after" if ![check_exit_status gssclient2] { @@ -519,7 +519,7 @@ proc doit { } { # Start the client with client identity 3 set env(KRB5CCNAME) $tmppwd/gss_tk_3 verbose "KRB5CCNAME=$env(KRB5CCNAME)" - spawn $GSSCLIENT -port 5557 -v2 $hostname gssservice@$hostname "message from gsstest3" + spawn $GSSCLIENT -port 5557 $hostname gssservice@$hostname "message from gsstest3" expect_after { -i $gss_server_spawn_id timeout { @@ -533,7 +533,7 @@ proc doit { } { return } } - expect -i $gss_server_spawn_id "Accepted connection: \"gsstest3@$REALMNAME\" at" + expect -i $gss_server_spawn_id "Accepted connection: \"gsstest3@$REALMNAME\"" expect -i $gss_server_spawn_id "Received message: \"message from gsstest3\"" catch "expect_after" expect_after { diff --git a/src/tests/dejagnu/krb-standalone/gssftp.exp b/src/tests/dejagnu/krb-standalone/gssftp.exp index ac67599ff..748793ad1 100644 --- a/src/tests/dejagnu/krb-standalone/gssftp.exp +++ b/src/tests/dejagnu/krb-standalone/gssftp.exp @@ -176,7 +176,7 @@ proc ftp_test { } { set testname "ftp connection" expect "Connected to $hostname" - expect -re "$localhostname.*FTP server .Version \[0-9.\]*. ready." + expect -nocase -re "$localhostname.*ftp server .version \[0-9.\]*. ready." expect -re "Using authentication type GSSAPI; ADAT must follow" expect "GSSAPI accepted as authentication type" expect { diff --git a/src/tests/gssapi/t_imp_name.c b/src/tests/gssapi/t_imp_name.c index f1b3cd2ad..d63faced3 100644 --- a/src/tests/gssapi/t_imp_name.c +++ b/src/tests/gssapi/t_imp_name.c @@ -83,7 +83,6 @@ static int test_import_name(name) printf("\n"); (void) gss_release_buffer(&min_stat, &buffer_name); - (void) gss_release_oid(&min_stat, &name_oid); (void) gss_release_name(&min_stat, &gss_name); return 0; } diff --git a/src/util/ChangeLog b/src/util/ChangeLog index 8540d06a5..ab39db25d 100644 --- a/src/util/ChangeLog +++ b/src/util/ChangeLog @@ -1,3 +1,15 @@ +Wed Jul 10 00:52:28 1996 Marc Horowitz + + * Makefile.in (all-unix): "install" the db2 headers and libs with + symlinks. db2 has its own self-contained autoconf setup, so this + is necessary here. + (all-unix): before installing db2, remove the links, so ln won't + fail the second time. + +Tue Jul 9 19:29:12 1996 Marc Horowitz + + * configure.in (CONFIG_DIRS): always build db2 + Mon May 20 11:05:49 1996 Tom Yu * libupdate.sh: allow to deal with multiple directories at once diff --git a/src/util/Makefile.in b/src/util/Makefile.in index 1d0a278d2..0faedfa1e 100644 --- a/src/util/Makefile.in +++ b/src/util/Makefile.in @@ -11,6 +11,20 @@ all-unix:: libupdate makeshlib all-mac:: all-windows: libupdate +# this is necessary because the db2 build, which is intended to be +# standalone, of course does not know to make symlinks in the right +# magic places in the krb5 build tree so other parts of the tree can +# find db2. So we make those links here. this can't use CopyHeader, +# because there's no way in this make setup to do stuff after the +# subdirectory recursion. fortunately, one does not need a +# destination to make a link... + +all-unix:: + $(RM) ../include/db.h ../include/db-config.h ../lib/libdb.a + ln -s ../util/db2/obj/db.h ../include + ln -s ../util/db2/obj/db-config.h ../include + ln -s ../util/db2/obj/libdb.a ../lib + unixmac: libupdate libupdate: $(srcdir)/libupdate.sh diff --git a/src/util/autoconf/ChangeLog b/src/util/autoconf/ChangeLog index 67827c8e1..e317fe91e 100644 --- a/src/util/autoconf/ChangeLog +++ b/src/util/autoconf/ChangeLog @@ -1,3 +1,12 @@ +Tue Jul 9 18:24:12 1996 Marc Horowitz + + * autoheader.sh (TEMPLATES): add ./acconfig.h to the list of + header templates used. This is necessary when a subpackage is + being autoreconf'd. + + * acgeneral.m4 (AC_CACHE_CHECK): added, from autoconf 2.7. db2 + needs it. + Wed Jun 12 18:17:17 1996 Tom Yu * autoconf.texi (Output): document changes to AC_OUTPUT diff --git a/src/util/autoconf/README.krb5 b/src/util/autoconf/README.krb5 new file mode 100644 index 000000000..d4b734658 --- /dev/null +++ b/src/util/autoconf/README.krb5 @@ -0,0 +1,2 @@ +There have been a few local changes and bug-fixes made to this tree. +Check out the ChangeLog file for details. diff --git a/src/util/autoconf/autoconf.info b/src/util/autoconf/autoconf.info index 050a253b8..472783f0b 100644 --- a/src/util/autoconf/autoconf.info +++ b/src/util/autoconf/autoconf.info @@ -1,5 +1,5 @@ -This is Info file ../autoconf.info, produced by Makeinfo-1.63 from the -input file ../autoconf.texi. +This is Info file autoconf.info, produced by Makeinfo-1.55 from the +input file ./autoconf.texi. START-INFO-DIR-ENTRY * Autoconf: (autoconf). Create source code configuration scripts. @@ -722,6 +722,10 @@ macro is `AC_INIT' (*note Input::.). separated by a colon. For example, AC_OUTPUT(Makefile:templates/top.mk lib/Makefile:templates/lib.mk) + You can also generate an output file from multiple input files by + separating the input files by a plus sign. For example, + AC_OUTPUT(Makefile:templates/pre.in+Makefile.in+templates/post.in) + If you pass EXTRA-CMDS, those commands will be inserted into `config.status' to be run after all its other processing. If INIT-CMDS are given, they are inserted just before EXTRA-CMDS, @@ -3024,7 +3028,7 @@ automatically. Notify the user of an error that prevents `configure' from completing. This macro prints an error message on the standard error output and exits `configure' with a nonzero status. - ERROR-DESCRIPTION should be something like `invalid value $HOME + eRROR-DESCRIPTION should be something like `invalid value $HOME for \$HOME'. - Macro: AC_MSG_WARN (PROBLEM-DESCRIPTION) @@ -3261,7 +3265,7 @@ it is considered obsolete. - Macro: AC_PROVIDE (THIS-MACRO-NAME) Record the fact that THIS-MACRO-NAME has been called. - THIS-MACRO-NAME should be the name of the macro that is calling + tHIS-MACRO-NAME should be the name of the macro that is calling `AC_PROVIDE'. An easy way to get it is from the `m4' builtin variable `$0', like this: @@ -4953,11 +4957,11 @@ Autoconf checks. * Menu: -* AC_MACRODIR <1>: Invoking autoupdate. -* AC_MACRODIR <1>: Invoking autoheader. -* AC_MACRODIR <1>: Invoking autoreconf. -* AC_MACRODIR <1>: Invoking autoconf. -* AC_MACRODIR <1>: Invoking ifnames. +* AC_MACRODIR: Invoking autoupdate. +* AC_MACRODIR: Invoking autoheader. +* AC_MACRODIR: Invoking autoreconf. +* AC_MACRODIR: Invoking autoconf. +* AC_MACRODIR: Invoking ifnames. * AC_MACRODIR: Invoking autoscan. * CONFIG_FILES: Invoking config.status. * CONFIG_HEADERS: Invoking config.status. @@ -4987,17 +4991,18 @@ how this is done. * build_cpu: System Type Variables. * build_os: System Type Variables. * build_vendor: System Type Variables. -* CC <1>: UNIX Variants. +* CC: UNIX Variants. +* CC: Particular Programs. * CC: Particular Programs. -* CFLAGS <1>: Particular Programs. * CFLAGS: Preset Output Variables. +* CFLAGS: Particular Programs. * configure_input: Preset Output Variables. * CPP: Particular Programs. * CPPFLAGS: Preset Output Variables. * CXX: Particular Programs. * CXXCPP: Particular Programs. -* CXXFLAGS <1>: Particular Programs. * CXXFLAGS: Preset Output Variables. +* CXXFLAGS: Particular Programs. * datadir: Preset Output Variables. * DEFS: Preset Output Variables. * exec_prefix: Preset Output Variables. @@ -5014,15 +5019,17 @@ how this is done. * KMEM_GROUP: Particular Functions. * LDFLAGS: Preset Output Variables. * LEX: Particular Programs. -* LEX_OUTPUT_ROOT: Particular Programs. * LEXLIB: Particular Programs. +* LEX_OUTPUT_ROOT: Particular Programs. * libdir: Preset Output Variables. * libexecdir: Preset Output Variables. -* LIBOBJS <1>: Structures. -* LIBOBJS <1>: Generic Functions. * LIBOBJS: Particular Functions. -* LIBS <1>: UNIX Variants. +* LIBOBJS: Particular Functions. +* LIBOBJS: Generic Functions. +* LIBOBJS: Structures. * LIBS: Preset Output Variables. +* LIBS: UNIX Variants. +* LIBS: UNIX Variants. * LN_S: Particular Programs. * localstatedir: Preset Output Variables. * mandir: Preset Output Variables. @@ -5061,15 +5068,9 @@ use these names in `#if' directives. * Menu: -* __CHAR_UNSIGNED__: Compiler Characteristics. -* _ALL_SOURCE: UNIX Variants. -* _MINIX: UNIX Variants. -* _POSIX_1_SOURCE: UNIX Variants. -* _POSIX_SOURCE: UNIX Variants. -* _POSIX_VERSION: Particular Headers. -* C_ALLOCA: Particular Functions. * CLOSEDIR_VOID: Particular Functions. * const: Compiler Characteristics. +* C_ALLOCA: Particular Functions. * DGUX: Particular Functions. * DIRENT: Particular Headers. * GETGROUPS_T: Particular Typedefs. @@ -5088,11 +5089,11 @@ use these names in `#if' directives. * HAVE_MMAP: Particular Functions. * HAVE_NDIR_H: Particular Headers. * HAVE_RESTARTABLE_SYSCALLS: System Services. +* HAVE_STRCOLL: Particular Functions. +* HAVE_STRFTIME: Particular Functions. * HAVE_ST_BLKSIZE: Structures. * HAVE_ST_BLOCKS: Structures. * HAVE_ST_RDEV: Structures. -* HAVE_STRCOLL: Particular Functions. -* HAVE_STRFTIME: Particular Functions. * HAVE_SYS_DIR_H: Particular Headers. * HAVE_SYS_NDIR_H: Particular Headers. * HAVE_SYS_WAIT_H: Particular Headers. @@ -5122,9 +5123,9 @@ use these names in `#if' directives. * size_t: Particular Typedefs. * STDC_HEADERS: Particular Headers. * SVR4: Particular Functions. -* SYS_SIGLIST_DECLARED: Particular Headers. * SYSDIR: Particular Headers. * SYSNDIR: Particular Headers. +* SYS_SIGLIST_DECLARED: Particular Headers. * TIME_WITH_SYS_TIME: Structures. * TM_IN_SYS_TIME: Structures. * uid_t: Particular Typedefs. @@ -5135,6 +5136,13 @@ use these names in `#if' directives. * VOID_CLOSEDIR: Particular Headers. * WORDS_BIGENDIAN: Compiler Characteristics. * YYTEXT_POINTER: Particular Programs. +* _ALL_SOURCE: UNIX Variants. +* _MINIX: UNIX Variants. +* _POSIX_1_SOURCE: UNIX Variants. +* _POSIX_SOURCE: UNIX Variants. +* _POSIX_SOURCE: UNIX Variants. +* _POSIX_VERSION: Particular Headers. +* __CHAR_UNSIGNED__: Compiler Characteristics.  File: autoconf.info, Node: Macro Index, Prev: Preprocessor Symbol Index, Up: Top @@ -5154,17 +5162,12 @@ list easier to use, the macros are listed without their preceding `AC_'. * ARG_PROGRAM: Transforming Names. * ARG_WITH: External Software. * BEFORE: Suggested Ordering. -* C_BIGENDIAN: Compiler Characteristics. -* C_CHAR_UNSIGNED: Compiler Characteristics. -* C_CONST: Compiler Characteristics. -* C_CROSS: Test Programs. -* C_INLINE: Compiler Characteristics. -* C_LONG_DOUBLE: Compiler Characteristics. * CACHE_CHECK: Caching Results. * CACHE_VAL: Caching Results. * CANONICAL_HOST: Canonicalizing. * CANONICAL_SYSTEM: Canonicalizing. * CHAR_UNSIGNED: Old Macro Names. +* CHECKING: Printing Messages. * CHECK_FUNC: Generic Functions. * CHECK_FUNCS: Generic Functions. * CHECK_HEADER: Generic Headers. @@ -5175,13 +5178,18 @@ list easier to use, the macros are listed without their preceding `AC_'. * CHECK_SIZEOF: Compiler Characteristics. * CHECK_TOOL: Generic Programs. * CHECK_TYPE: Generic Typedefs. -* CHECKING: Printing Messages. * COMPILE_CHECK: Examining Libraries. * CONFIG_AUX_DIR: Input. * CONFIG_HEADER: Configuration Headers. * CONFIG_SUBDIRS: Subdirectories. * CONST: Old Macro Names. * CROSS_CHECK: Old Macro Names. +* C_BIGENDIAN: Compiler Characteristics. +* C_CHAR_UNSIGNED: Compiler Characteristics. +* C_CONST: Compiler Characteristics. +* C_CROSS: Test Programs. +* C_INLINE: Compiler Characteristics. +* C_LONG_DOUBLE: Compiler Characteristics. * DECL_SYS_SIGLIST: Particular Headers. * DECL_YYTEXT: Particular Programs. * DEFINE: Defining Symbols. @@ -5260,6 +5268,11 @@ list easier to use, the macros are listed without their preceding `AC_'. * PREFIX: Old Macro Names. * PREFIX_PROGRAM: Default Prefix. * PREREQ: Versions. +* PROGRAMS_CHECK: Old Macro Names. +* PROGRAMS_PATH: Old Macro Names. +* PROGRAM_CHECK: Old Macro Names. +* PROGRAM_EGREP: Old Macro Names. +* PROGRAM_PATH: Old Macro Names. * PROG_AWK: Particular Programs. * PROG_CC: Particular Programs. * PROG_CC_C_O: Particular Programs. @@ -5273,11 +5286,6 @@ list easier to use, the macros are listed without their preceding `AC_'. * PROG_MAKE_SET: Output. * PROG_RANLIB: Particular Programs. * PROG_YACC: Particular Programs. -* PROGRAM_CHECK: Old Macro Names. -* PROGRAM_EGREP: Old Macro Names. -* PROGRAM_PATH: Old Macro Names. -* PROGRAMS_CHECK: Old Macro Names. -* PROGRAMS_PATH: Old Macro Names. * PROVIDE: Prerequisite Macros. * REMOTE_TAPE: Old Macro Names. * REPLACE_FUNCS: Generic Functions. @@ -5288,15 +5296,12 @@ list easier to use, the macros are listed without their preceding `AC_'. * REVISION: Versions. * RSH: Old Macro Names. * SCO_INTL: UNIX Variants. -* SET_MAKE: Old Macro Names. * SETVBUF_REVERSED: Old Macro Names. -* SIZE_T: Old Macro Names. +* SET_MAKE: Old Macro Names. * SIZEOF_TYPE: Old Macro Names. -* ST_BLKSIZE: Old Macro Names. -* ST_BLOCKS: Old Macro Names. -* ST_RDEV: Old Macro Names. -* STAT_MACROS_BROKEN <1>: Old Macro Names. +* SIZE_T: Old Macro Names. * STAT_MACROS_BROKEN: Structures. +* STAT_MACROS_BROKEN: Old Macro Names. * STDC_HEADERS: Old Macro Names. * STRCOLL: Old Macro Names. * STRUCT_ST_BLKSIZE: Structures. @@ -5304,6 +5309,9 @@ list easier to use, the macros are listed without their preceding `AC_'. * STRUCT_ST_RDEV: Structures. * STRUCT_TIMEZONE: Structures. * STRUCT_TM: Structures. +* ST_BLKSIZE: Old Macro Names. +* ST_BLOCKS: Old Macro Names. +* ST_RDEV: Old Macro Names. * SUBST: Setting Output Variables. * SUBST_FILE: Setting Output Variables. * SYS_INTERPRETER: System Services. @@ -5312,8 +5320,8 @@ list easier to use, the macros are listed without their preceding `AC_'. * SYS_SIGLIST_DECLARED: Old Macro Names. * TEST_CPP: Old Macro Names. * TEST_PROGRAM: Old Macro Names. -* TIME_WITH_SYS_TIME: Old Macro Names. * TIMEZONE: Old Macro Names. +* TIME_WITH_SYS_TIME: Old Macro Names. * TRY_COMPILE: Examining Syntax. * TRY_CPP: Examining Declarations. * TRY_LINK: Examining Libraries. @@ -5342,118 +5350,118 @@ list easier to use, the macros are listed without their preceding `AC_'.  Tag Table: -Node: Top1177 -Node: Introduction9316 -Node: Making configure Scripts13156 -Node: Writing configure.in16221 -Node: Invoking autoscan19921 -Node: Invoking ifnames22226 -Node: Invoking autoconf23716 -Node: Invoking autoreconf25554 -Node: Setup27874 -Node: Input28760 -Node: Output30483 -Node: Makefile Substitutions33233 -Node: Preset Output Variables34817 -Node: Build Directories39319 -Node: Automatic Remaking40952 -Node: Configuration Headers43038 -Node: Header Templates45405 -Node: Invoking autoheader46584 -Node: Subdirectories49732 -Node: Default Prefix51075 -Node: Versions52479 -Node: Existing Tests54383 -Node: Alternative Programs55848 -Node: Particular Programs56512 -Node: Generic Programs62362 -Node: Libraries65188 -Node: Library Functions67017 -Node: Particular Functions67575 -Node: Generic Functions73878 -Node: Header Files75721 -Node: Particular Headers76280 -Node: Generic Headers83251 -Node: Structures84553 -Node: Typedefs86790 -Node: Particular Typedefs87294 -Node: Generic Typedefs88494 -Node: Compiler Characteristics88937 -Node: System Services91562 -Node: UNIX Variants93911 -Node: Writing Tests95930 -Node: Examining Declarations97896 -Node: Examining Syntax100366 -Node: Examining Libraries101423 -Node: Run Time103982 -Node: Test Programs104943 -Node: Guidelines107480 -Node: Test Functions108669 -Node: Portable Shell110212 -Node: Testing Values and Files112144 -Node: Multiple Cases113799 -Node: Language Choice114997 -Node: Results116555 -Node: Defining Symbols117314 -Node: Setting Output Variables120323 -Node: Caching Results122169 -Node: Cache Variable Names124450 -Node: Cache Files125999 -Node: Printing Messages128097 -Node: Writing Macros131384 -Node: Macro Definitions132003 -Node: Macro Names133108 -Node: Quoting135559 -Node: Dependencies Between Macros137461 -Node: Prerequisite Macros138093 -Node: Suggested Ordering139548 -Node: Obsolete Macros141078 -Node: Manual Configuration142302 -Node: Specifying Names143201 -Node: Canonicalizing145073 -Node: System Type Variables146387 -Node: Using System Type147134 -Node: Site Configuration148581 -Node: External Software149354 -Node: Package Options152052 -Node: Site Details154319 -Node: Transforming Names155542 -Node: Transformation Options156720 -Node: Transformation Examples157186 -Node: Transformation Rules158754 -Node: Site Defaults160163 -Node: Invoking configure163947 -Node: Basic Installation164896 -Node: Compilers and Options167476 -Node: Multiple Architectures168125 -Node: Installation Names169111 -Node: Optional Features170295 -Node: System Type171065 -Node: Sharing Defaults172087 -Node: Operation Controls172711 -Node: Invoking config.status173586 -Node: Questions176974 -Node: Distributing177506 -Node: Why GNU m4178632 -Node: Bootstrapping179445 -Node: Why Not Imake180061 -Node: Upgrading184470 -Node: Changed File Names185991 -Node: Changed Makefiles186727 -Node: Changed Macros187823 -Node: Invoking autoupdate189070 -Node: Changed Results190661 -Node: Changed Macro Writing192763 -Node: History194026 -Node: Genesis194733 -Node: Exodus195906 -Node: Leviticus198955 -Node: Numbers200478 -Node: Deuteronomy202394 -Node: Old Macro Names205058 -Node: Environment Variable Index208107 -Node: Output Variable Index209109 -Node: Preprocessor Symbol Index213792 -Node: Macro Index218607 +Node: Top1173 +Node: Introduction9312 +Node: Making configure Scripts13152 +Node: Writing configure.in16217 +Node: Invoking autoscan19917 +Node: Invoking ifnames22222 +Node: Invoking autoconf23712 +Node: Invoking autoreconf25550 +Node: Setup27870 +Node: Input28756 +Node: Output30479 +Node: Makefile Substitutions33440 +Node: Preset Output Variables35024 +Node: Build Directories39526 +Node: Automatic Remaking41159 +Node: Configuration Headers43245 +Node: Header Templates45612 +Node: Invoking autoheader46791 +Node: Subdirectories49939 +Node: Default Prefix51282 +Node: Versions52686 +Node: Existing Tests54590 +Node: Alternative Programs56055 +Node: Particular Programs56719 +Node: Generic Programs62569 +Node: Libraries65395 +Node: Library Functions67224 +Node: Particular Functions67782 +Node: Generic Functions74085 +Node: Header Files75928 +Node: Particular Headers76487 +Node: Generic Headers83458 +Node: Structures84760 +Node: Typedefs86997 +Node: Particular Typedefs87501 +Node: Generic Typedefs88701 +Node: Compiler Characteristics89144 +Node: System Services91769 +Node: UNIX Variants94118 +Node: Writing Tests96137 +Node: Examining Declarations98103 +Node: Examining Syntax100573 +Node: Examining Libraries101630 +Node: Run Time104189 +Node: Test Programs105150 +Node: Guidelines107687 +Node: Test Functions108876 +Node: Portable Shell110419 +Node: Testing Values and Files112351 +Node: Multiple Cases114006 +Node: Language Choice115204 +Node: Results116762 +Node: Defining Symbols117521 +Node: Setting Output Variables120530 +Node: Caching Results122376 +Node: Cache Variable Names124657 +Node: Cache Files126206 +Node: Printing Messages128304 +Node: Writing Macros131591 +Node: Macro Definitions132210 +Node: Macro Names133315 +Node: Quoting135766 +Node: Dependencies Between Macros137668 +Node: Prerequisite Macros138300 +Node: Suggested Ordering139755 +Node: Obsolete Macros141285 +Node: Manual Configuration142509 +Node: Specifying Names143408 +Node: Canonicalizing145280 +Node: System Type Variables146594 +Node: Using System Type147341 +Node: Site Configuration148788 +Node: External Software149561 +Node: Package Options152259 +Node: Site Details154526 +Node: Transforming Names155749 +Node: Transformation Options156927 +Node: Transformation Examples157393 +Node: Transformation Rules158961 +Node: Site Defaults160370 +Node: Invoking configure164154 +Node: Basic Installation165103 +Node: Compilers and Options167683 +Node: Multiple Architectures168332 +Node: Installation Names169318 +Node: Optional Features170502 +Node: System Type171272 +Node: Sharing Defaults172294 +Node: Operation Controls172918 +Node: Invoking config.status173793 +Node: Questions177181 +Node: Distributing177713 +Node: Why GNU m4178839 +Node: Bootstrapping179652 +Node: Why Not Imake180268 +Node: Upgrading184677 +Node: Changed File Names186198 +Node: Changed Makefiles186934 +Node: Changed Macros188030 +Node: Invoking autoupdate189277 +Node: Changed Results190868 +Node: Changed Macro Writing192970 +Node: History194233 +Node: Genesis194940 +Node: Exodus196113 +Node: Leviticus199162 +Node: Numbers200685 +Node: Deuteronomy202601 +Node: Old Macro Names205265 +Node: Environment Variable Index208314 +Node: Output Variable Index209316 +Node: Preprocessor Symbol Index214177 +Node: Macro Index219047  End Tag Table diff --git a/src/util/autoconf/autoheader.sh b/src/util/autoconf/autoheader.sh index abbc98350..a9aa44693 100644 --- a/src/util/autoconf/autoheader.sh +++ b/src/util/autoconf/autoheader.sh @@ -85,6 +85,7 @@ if test $show_version = yes; then fi TEMPLATES="${AC_MACRODIR}/acconfig.h" +test -r ./acconfig.h && TEMPLATES="${TEMPLATES} ./acconfig.h" test -r $localdir/acconfig.h && TEMPLATES="${TEMPLATES} $localdir/acconfig.h" case $# in diff --git a/src/util/autoconf/configure b/src/util/autoconf/configure index 65ce229d5..d5f3ea864 100644 --- a/src/util/autoconf/configure +++ b/src/util/autoconf/configure @@ -906,7 +906,19 @@ for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then esac echo creating "$ac_file" rm -f "$ac_file" - configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + # allow for outfile[:infile1[+infile2[+infile3...]]] syntax + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}+" + ac_files_in= + for ac_file_name in $ac_file_in + do + ac_files_in="$ac_files_in $ac_given_srcdir/$ac_file_name" + done + IFS="$ac_save_ifs" + configure_input=`echo $ac_files_in | sed 's%/./%/%g +s% *./%%g +s% *%+%g +s%^%Generated automatically from % +s%$% by configure.%'` case "$ac_file" in *Makefile*) ac_comsub="1i\\ # $configure_input" ;; @@ -917,7 +929,7 @@ s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g s%@INSTALL@%$INSTALL%g -" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file +" -f conftest.subs $ac_files_in > $ac_file fi; done rm -f conftest.subs diff --git a/src/util/autoconf/standards.info b/src/util/autoconf/standards.info index c673f9261..a51f1e2d1 100644 --- a/src/util/autoconf/standards.info +++ b/src/util/autoconf/standards.info @@ -1,4 +1,4 @@ -This is Info file standards.info, produced by Makeinfo-1.63 from the +This is Info file standards.info, produced by Makeinfo-1.55 from the input file ./standards.texi. START-INFO-DIR-ENTRY diff --git a/src/util/configure.in b/src/util/configure.in index 77abe3b78..ef2f3635e 100644 --- a/src/util/configure.in +++ b/src/util/configure.in @@ -18,15 +18,6 @@ esac SHLIB_TAIL_COMP=$krb5_cv_shlibs_tail_comp AC_SUBST(SHLIB_TAIL_COMP) -dbdir= -WITH_ANAME_DB -if test "$dbval" = db; then - db=berk_db -else - WITH_KDB_DB - if test "$dbval" = db; then db=berk_db; fi -fi - -CONFIG_DIRS(et ss profile $db pty) +CONFIG_DIRS(et ss profile pty dyn db2) DO_SUBDIRS V5_AC_OUTPUT_MAKEFILE diff --git a/src/util/db2/CHANGELOG b/src/util/db2/CHANGELOG new file mode 100644 index 000000000..abd05f95d --- /dev/null +++ b/src/util/db2/CHANGELOG @@ -0,0 +1,123 @@ +db2-alpha.0 -> db2-alpha.1 + This fixes a number of bugs in the alpha release. + 1. 64-bit functionality. The test suite now runs on alphas. + Memory leak fixed. + Pairs no longer disappear when pages are exactly full. + Flush meta-data correctly on sync. +1.86 -> db2-alpha.0 + This is an interim release. We are in the process of + adding logging, locking, and transaction support to all + the db access methods. This will necessitate database + format changes, interface changes, and major upheaval. + In the meantime, this PRELIMINARY release is to correct + some known bugs in the hash pacakge. This release uses + a different page format from 1.86, so you will need to + dump and reload any existing databases if you upgrade + to this (and may have to do it again when 2.0 becomes + available. +1.85 -> 1.86 + btree: Fix to split code for single large record at the end of a + page. +1.84 -> 1.85 + recno: #ifdef out use of mmap, it's not portable enough. + +1.83 -> 1.84 Thu Aug 18 15:46:07 EDT 1994 + recno: Rework fixed-length records so that closing and reopening + the file now works. Pad short records on input. Never do + signed comparison in recno input reading functions. + +1.82 -> 1.83 Tue Jul 26 15:33:44 EDT 1994 + btree: Rework cursor deletion code yet again; bugs with + deleting empty pages that only contained the cursor + record. + +1.81 -> 1.82 Sat Jul 16 11:01:50 EDT 1994 + btree: Fix bugs introduced by new cursor/deletion code. + Replace return kbuf/dbuf with real DBT's. + +1.80 -> 1.81 + btree: Fix bugs introduced by new cursor/deletion code. + all: Add #defines for Purify. + +1.79 -> 1.80 Wed Jul 13 22:41:54 EDT 1994 + btree Change deletion to coalesce empty pages. This is a major + change, cursors and duplicate pages all had to be reworked. + Return to a fixed stack. + recno: Affected by cursor changes. New cursor structures should + permit multiple cursors in the future. + +1.78 -> 1.79 Mon Jun 20 17:36:47 EDT 1994 + all: Minor cleanups of 1.78 for porting reasons; only + major change was inlining check of NULL pointer + so that __fix_realloc goes away. + +1.77 -> 1.78 Thu Jun 16 19:06:43 EDT 1994 + all: Move "standard" size typedef's into db.h. + +1.76 -> 1.77 Thu Jun 16 16:48:38 EDT 1994 + hash: Delete __init_ routine, has special meaning to OSF 2.0. + +1.74 -> 1.76 + all: Finish up the port to the Alpha. + +1.73 -> 1.74 + recno: Don't put the record if rec_search fails, in rec_rdelete. + Create fixed-length intermediate records past "end" of DB + correctly. + Realloc bug when reading in fixed records. + all: First cut at port to Alpha (64-bit architecture) using + 4.4BSD basic integral types typedef's. + Cast allocation pointers to shut up old compilers. + Rework PORT directory into OS/machine directories. + +1.72 -> 1.73 + btree: If enough duplicate records were inserted and then deleted + that internal pages had references to empty pages of the + duplicate keys, the search function ended up on the wrong + page. + +1.7 -> 1.72 12 Oct 1993 + hash: Support NET/2 hash formats. + +1.7 -> 1.71 16 Sep 1993 + btree/recno: + Fix bug in internal search routines that caused + return of invalid pointers. + +1.6 -> 1.7 07 Sep 1993 + hash: Fixed big key overflow bugs. + test: Portability hacks, rewrite test script, Makefile. + btree/recno: + Stop copying non-overflow key/data pairs. + PORT: Break PORT directory up into per architecture/OS + subdirectories. + +1.5 -> 1.6 06 Jun 1993 + hash: In PAIRFITS, the first comparison should look at (P)[2]. + The hash_realloc function was walking off the end of memory. + The overflow page number was wrong when bumping splitpoint. + +1.4 -> 1.5 23 May 1993 + hash: Set hash default fill factor dynamically. + recno: Fixed bug in sorted page splits. + Add page size parameter support. + Allow recno to specify the name of the underlying btree; + used for vi recovery. + btree/recno: + Support 64K pages. + btree/hash/recno: + Provide access to an underlying file descriptor. + Change sync routines to take a flag argument, recno + uses this to sync out the underlying btree. + +1.3 -> 1.4 10 May 1993 + recno: Delete the R_CURSORLOG flag from the recno interface. + Zero-length record fix for non-mmap reads. + Try and make SIZE_T_MAX test in open portable. + +1.2 -> 1.3 01 May 1993 + btree: Ignore user byte-order setting when reading already + existing database. Fixes to byte-order conversions. + +1.1 -> 1.2 15 Apr 1993 + No bug fixes, only compatibility hacks. diff --git a/src/util/db2/ChangeLog b/src/util/db2/ChangeLog new file mode 100644 index 000000000..9d240ec94 --- /dev/null +++ b/src/util/db2/ChangeLog @@ -0,0 +1,77 @@ +Fri Jun 21 00:07:57 1996 Marc Horowitz + + * hash/dbm.c (delete, store): dbm_rdonly() doesn't exist on some + systems. In addition, the handle is really a DB handle, so it + would break if it did exist. Remove calls to it. + +Wed Apr 10 21:39:54 1996 Marc Horowitz + + * hash/hash_page.c (__addel): It is possible to damage a page if a + bigpair is added and there's not enough room. Check to make sure + there's enough room before adding anything. + + * hash/hash.c (hdestroy, cursor_delete): there were still a few + things in the hashp which weren't being freed, causing a small + memory leak. + +Sun Apr 7 01:40:54 1996 Marc Horowitz + + * clib/mk{,s}temp.c: renamed to accurately reflect the function + being provided (ultrix 4.2 has one, but not the other). + + * [way too many files to list here]: rename pgno_t to db_pgno_t, + since this symbol is defined in on at least one OS to + a non-compatible type (irix 5.2 defines it as long; db wants it to + be u_int32_t). + + * hash/dbm.c, include/db-ndbm.h: use and reference the compat + ndbm.h file + + * btree/bt_open.c, hash/hash.c, hash/hash_page.c, + include/db-int.h, include/db.h: build fixes - use configure to set + db internal cpp symbols for endianness stuff, move __P definition + from db-int.h to db.h. + + * configure.in, acconfig.h, Makefile.in, obj/configure.in, + obj/acconfig.in, obj/Makefile.in: rearrange the configure inputs + to deal properly with configure at the top level, and with a + multiarchitecture build using VPATH + +Sat Apr 6 16:43:26 1996 Marc Horowitz + + * obj/Makefile.in: random cleanup + + * btree/*.c db/db.c hash/*.c mpool/mpool.c recno/*.c + test/SEQ_TEST/t.c test/dbtest.c test/*/*.c: use "db-int.h" instead + of "db.h". + + * include/db.h, include/db-int.h: rototilled to be portable and + sensible, using configure whenever possible. + + * btree/*.c db/db.c hash/*.c mpool/mpool.c recno/*.c + test/SEQ_TEST/t.c test/dbtest.c test/*/*.c: use "db.h" instead of + . + + * hash/hash.h, btree/btree.h, mpool/mpool.c: #include "mpool.h" + instead of . + + * test/hash1.tests/thash4.c: remove unused and nonportable + + + * test/hash2.tests/bigtest.c: replace with + + * clib/memmove.c: remove + + * mpool/mpool.c, mpool/mpool.h, hash/hash.h, include/db-queue.h: + include "db-queue.h" instead of , since it's not part + of any OS standard. + + * obj/*: first attempt at autoconfiscation + + * test/hash1.tests/driver2.c (main), test/hash1.tests/tseq.c + (main): replace berkeley memoryisms with ansi ones. + + * btree/bt_open.c (tmp): use sprintf instead of snprintf(). + conditionalize signal stuff on SIG_BLOCK instead of using special + magic in a header file. + diff --git a/src/util/db2/Makefile.in b/src/util/db2/Makefile.in new file mode 100644 index 000000000..3e6344baa --- /dev/null +++ b/src/util/db2/Makefile.in @@ -0,0 +1,12 @@ +SHELL = /bin/sh + +SUBDIRS = obj + +all clean distclean realclean check:: + for i in ${SUBDIRS}; do (cd $$i; ${MAKE} $@) || exit 1; done + +distclean:: + rm -rf config.status config.log config.cache db-config.h + +realclean:: + rm -rf configure db-config.h.in diff --git a/src/util/db2/Makefile.inc b/src/util/db2/Makefile.inc new file mode 100644 index 000000000..77af9c512 --- /dev/null +++ b/src/util/db2/Makefile.inc @@ -0,0 +1,10 @@ +# @(#)Makefile.inc 8.2 (Berkeley) 2/21/94 +# +CFLAGS+=-D__DBINTERFACE_PRIVATE + +.include "${.CURDIR}/db/btree/Makefile.inc" +.include "${.CURDIR}/db/db/Makefile.inc" +.include "${.CURDIR}/db/hash/Makefile.inc" +.include "${.CURDIR}/db/man/Makefile.inc" +.include "${.CURDIR}/db/mpool/Makefile.inc" +.include "${.CURDIR}/db/recno/Makefile.inc" diff --git a/src/util/db2/README b/src/util/db2/README new file mode 100644 index 000000000..5700b7393 --- /dev/null +++ b/src/util/db2/README @@ -0,0 +1,41 @@ +# @(#)README 8.28 (Berkeley) 11/2/95 + +This is version 2.0-ALPHA of the Berkeley DB code. +THIS IS A PRELIMINARY RELEASE. + +For information on compiling and installing this software, see the file +PORT/README. + +Newer versions of this software will periodically be made available by +anonymous ftp from ftp.cs.berkeley.edu:ucb/4bsd/db.tar.{Z,gz} and from +ftp.harvard.edu:margo/db.tar.{Z,gz}. If you want to receive announcements +of future releases of this software, send email to the contact address +below. + +Email questions may be addressed to dbinfo@eecs.harvard.edu. + +============================================ +Distribution contents: + +README This file. +CHANGELOG List of changes, per version. +btree B+tree access method. +db The db_open interface routine. +docs Various USENIX papers, and the formatted manual pages. +hash Extended linear hashing access method. +lock Lock manager. +log Log manager. +man The unformatted manual pages. +mpool The buffer manager support. +mutex Mutex support. +recno The fixed/variable length record access method. +test Test package. +txn Transaction support. + +============================================ +Debugging: + +If you're running a memory checker (e.g. Purify) on DB, make sure that +you recompile it with "-DPURIFY" in the CFLAGS, first. By default, +allocated pages are not initialized by the DB code, and they will show +up as reads of uninitialized memory in the buffer write routines. diff --git a/src/util/db2/acconfig.h b/src/util/db2/acconfig.h new file mode 100644 index 000000000..d36e23fe2 --- /dev/null +++ b/src/util/db2/acconfig.h @@ -0,0 +1,17 @@ +#undef ssize_t + +/* BSD4.3, non-posix types */ + +#undef u_char +#undef u_short +#undef u_int +#undef u_long + +/* sized types used by db internals */ + +#undef int8_t +#undef u_int8_t +#undef int16_t +#undef u_int16_t +#undef int32_t +#undef u_int32_t diff --git a/src/util/db2/btree/Makefile.inc b/src/util/db2/btree/Makefile.inc new file mode 100644 index 000000000..8ed76494a --- /dev/null +++ b/src/util/db2/btree/Makefile.inc @@ -0,0 +1,7 @@ +# @(#)Makefile.inc 8.2 (Berkeley) 7/14/94 + +.PATH: ${.CURDIR}/db/btree + +SRCS+= bt_close.c bt_conv.c bt_debug.c bt_delete.c bt_get.c bt_open.c \ + bt_overflow.c bt_page.c bt_put.c bt_search.c bt_seq.c bt_split.c \ + bt_utils.c diff --git a/src/util/db2/btree/bt_close.c b/src/util/db2/btree/bt_close.c new file mode 100644 index 000000000..b731dcb5d --- /dev/null +++ b/src/util/db2/btree/bt_close.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_close.c 8.7 (Berkeley) 8/17/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +static int bt_meta __P((BTREE *)); + +/* + * BT_CLOSE -- Close a btree. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_close(dbp) + DB *dbp; +{ + BTREE *t; + int fd; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Sync the tree. */ + if (__bt_sync(dbp, 0) == RET_ERROR) + return (RET_ERROR); + + /* Close the memory pool. */ + if (mpool_close(t->bt_mp) == RET_ERROR) + return (RET_ERROR); + + /* Free random memory. */ + if (t->bt_cursor.key.data != NULL) { + free(t->bt_cursor.key.data); + t->bt_cursor.key.size = 0; + t->bt_cursor.key.data = NULL; + } + if (t->bt_rkey.data) { + free(t->bt_rkey.data); + t->bt_rkey.size = 0; + t->bt_rkey.data = NULL; + } + if (t->bt_rdata.data) { + free(t->bt_rdata.data); + t->bt_rdata.size = 0; + t->bt_rdata.data = NULL; + } + + fd = t->bt_fd; + free(t); + free(dbp); + return (close(fd) ? RET_ERROR : RET_SUCCESS); +} + +/* + * BT_SYNC -- sync the btree to disk. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_sync(dbp, flags) + const DB *dbp; + u_int flags; +{ + BTREE *t; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Sync doesn't currently take any flags. */ + if (flags != 0) { + errno = EINVAL; + return (RET_ERROR); + } + + if (F_ISSET(t, B_INMEM | B_RDONLY) || !F_ISSET(t, B_MODIFIED)) + return (RET_SUCCESS); + + if (F_ISSET(t, B_METADIRTY) && bt_meta(t) == RET_ERROR) + return (RET_ERROR); + + if ((status = mpool_sync(t->bt_mp)) == RET_SUCCESS) + F_CLR(t, B_MODIFIED); + + return (status); +} + +/* + * BT_META -- write the tree meta data to disk. + * + * Parameters: + * t: tree + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_meta(t) + BTREE *t; +{ + BTMETA m; + void *p; + + if ((p = mpool_get(t->bt_mp, P_META, 0)) == NULL) + return (RET_ERROR); + + /* Fill in metadata. */ + m.magic = BTREEMAGIC; + m.version = BTREEVERSION; + m.psize = t->bt_psize; + m.free = t->bt_free; + m.nrecs = t->bt_nrecs; + m.flags = F_ISSET(t, SAVEMETA); + + memmove(p, &m, sizeof(BTMETA)); + mpool_put(t->bt_mp, p, MPOOL_DIRTY); + return (RET_SUCCESS); +} diff --git a/src/util/db2/btree/bt_conv.c b/src/util/db2/btree/bt_conv.c new file mode 100644 index 000000000..6cfa216ca --- /dev/null +++ b/src/util/db2/btree/bt_conv.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_conv.c 8.5 (Berkeley) 8/17/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +#include "db-int.h" +#include "btree.h" + +static void mswap __P((PAGE *)); + +/* + * __BT_BPGIN, __BT_BPGOUT -- + * Convert host-specific number layout to/from the host-independent + * format stored on disk. + * + * Parameters: + * t: tree + * pg: page number + * h: page to convert + */ +void +__bt_pgin(t, pg, pp) + void *t; + db_pgno_t pg; + void *pp; +{ + PAGE *h; + indx_t i, top; + u_char flags; + char *p; + + if (!F_ISSET(((BTREE *)t), B_NEEDSWAP)) + return; + if (pg == P_META) { + mswap(pp); + return; + } + + h = pp; + M_32_SWAP(h->pgno); + M_32_SWAP(h->prevpg); + M_32_SWAP(h->nextpg); + M_32_SWAP(h->flags); + M_16_SWAP(h->lower); + M_16_SWAP(h->upper); + + top = NEXTINDEX(h); + if ((h->flags & P_TYPE) == P_BINTERNAL) + for (i = 0; i < top; i++) { + M_16_SWAP(h->linp[i]); + p = (char *)GETBINTERNAL(h, i); + P_32_SWAP(p); + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + if (*(u_char *)p & P_BIGKEY) { + p += sizeof(u_char); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + } + else if ((h->flags & P_TYPE) == P_BLEAF) + for (i = 0; i < top; i++) { + M_16_SWAP(h->linp[i]); + p = (char *)GETBLEAF(h, i); + P_32_SWAP(p); + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(u_int32_t); + flags = *(u_char *)p; + if (flags & (P_BIGKEY | P_BIGDATA)) { + p += sizeof(u_char); + if (flags & P_BIGKEY) { + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + if (flags & P_BIGDATA) { + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + } + } +} + +void +__bt_pgout(t, pg, pp) + void *t; + db_pgno_t pg; + void *pp; +{ + PAGE *h; + indx_t i, top; + u_char flags; + char *p; + + if (!F_ISSET(((BTREE *)t), B_NEEDSWAP)) + return; + if (pg == P_META) { + mswap(pp); + return; + } + + h = pp; + top = NEXTINDEX(h); + if ((h->flags & P_TYPE) == P_BINTERNAL) + for (i = 0; i < top; i++) { + p = (char *)GETBINTERNAL(h, i); + P_32_SWAP(p); + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + if (*(u_char *)p & P_BIGKEY) { + p += sizeof(u_char); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + M_16_SWAP(h->linp[i]); + } + else if ((h->flags & P_TYPE) == P_BLEAF) + for (i = 0; i < top; i++) { + p = (char *)GETBLEAF(h, i); + P_32_SWAP(p); + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(u_int32_t); + flags = *(u_char *)p; + if (flags & (P_BIGKEY | P_BIGDATA)) { + p += sizeof(u_char); + if (flags & P_BIGKEY) { + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + if (flags & P_BIGDATA) { + p += sizeof(u_int32_t); + P_32_SWAP(p); + p += sizeof(db_pgno_t); + P_32_SWAP(p); + } + } + M_16_SWAP(h->linp[i]); + } + + M_32_SWAP(h->pgno); + M_32_SWAP(h->prevpg); + M_32_SWAP(h->nextpg); + M_32_SWAP(h->flags); + M_16_SWAP(h->lower); + M_16_SWAP(h->upper); +} + +/* + * MSWAP -- Actually swap the bytes on the meta page. + * + * Parameters: + * p: page to convert + */ +static void +mswap(pg) + PAGE *pg; +{ + char *p; + + p = (char *)pg; + P_32_SWAP(p); /* magic */ + p += sizeof(u_int32_t); + P_32_SWAP(p); /* version */ + p += sizeof(u_int32_t); + P_32_SWAP(p); /* psize */ + p += sizeof(u_int32_t); + P_32_SWAP(p); /* free */ + p += sizeof(u_int32_t); + P_32_SWAP(p); /* nrecs */ + p += sizeof(u_int32_t); + P_32_SWAP(p); /* flags */ + p += sizeof(u_int32_t); +} diff --git a/src/util/db2/btree/bt_debug.c b/src/util/db2/btree/bt_debug.c new file mode 100644 index 000000000..8cf1cdaf5 --- /dev/null +++ b/src/util/db2/btree/bt_debug.c @@ -0,0 +1,372 @@ +/*- + * Copyright (c) 1990, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_debug.c 8.6 (Berkeley) 1/9/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +#if defined(DEBUG) || defined(STATISTICS) + +static FILE *tracefp; + +/* + * __bt_dinit -- + * initialize debugging. + */ +static void +__bt_dinit() +{ + static int first = 1; + char buf[1024]; + + if (!first) + return; + first = 0; + +#ifndef TRACE_TO_STDERR + if ((tracefp = fopen("/tmp/__bt_debug", "w")) != NULL) + return; +#endif + tracefp = stderr; +} +#endif + +#ifdef DEBUG +/* + * __bt_dump -- + * dump the tree + * + * Parameters: + * dbp: pointer to the DB + */ +int +__bt_dump(dbp) + DB *dbp; +{ + BTREE *t; + PAGE *h; + db_pgno_t i; + char *sep; + + __bt_dinit(); + + t = dbp->internal; + (void)fprintf(tracefp, "%s: pgsz %d", + F_ISSET(t, B_INMEM) ? "memory" : "disk", t->bt_psize); + if (F_ISSET(t, R_RECNO)) + (void)fprintf(tracefp, " keys %lu", t->bt_nrecs); +#undef X +#define X(flag, name) \ + if (F_ISSET(t, flag)) { \ + (void)fprintf(tracefp, "%s%s", sep, name); \ + sep = ", "; \ + } + if (t->flags != 0) { + sep = " flags ("; + X(R_FIXLEN, "FIXLEN"); + X(B_INMEM, "INMEM"); + X(B_NODUPS, "NODUPS"); + X(B_RDONLY, "RDONLY"); + X(R_RECNO, "RECNO"); + X(B_METADIRTY,"METADIRTY"); + (void)fprintf(tracefp, ")\n"); + } +#undef X + + for (i = P_ROOT; + (h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN)) != NULL; ++i) + __bt_dpage(h); + (void)fflush(tracefp); + return (0); +} + +/* + * BT_DMPAGE -- Dump the meta page + * + * Parameters: + * h: pointer to the PAGE + */ +int +__bt_dmpage(h) + PAGE *h; +{ + BTMETA *m; + char *sep; + + __bt_dinit(); + + m = (BTMETA *)h; + (void)fprintf(tracefp, "magic %lx\n", m->magic); + (void)fprintf(tracefp, "version %lu\n", m->version); + (void)fprintf(tracefp, "psize %lu\n", m->psize); + (void)fprintf(tracefp, "free %lu\n", m->free); + (void)fprintf(tracefp, "nrecs %lu\n", m->nrecs); + (void)fprintf(tracefp, "flags %lu", m->flags); +#undef X +#define X(flag, name) \ + if (m->flags & flag) { \ + (void)fprintf(tracefp, "%s%s", sep, name); \ + sep = ", "; \ + } + if (m->flags) { + sep = " ("; + X(B_NODUPS, "NODUPS"); + X(R_RECNO, "RECNO"); + (void)fprintf(tracefp, ")"); + } + (void)fflush(tracefp); + return (0); +} + +/* + * BT_DNPAGE -- Dump the page + * + * Parameters: + * n: page number to dump. + */ +int +__bt_dnpage(dbp, pgno) + DB *dbp; + db_pgno_t pgno; +{ + BTREE *t; + PAGE *h; + + __bt_dinit(); + + t = dbp->internal; + if ((h = mpool_get(t->bt_mp, pgno, MPOOL_IGNOREPIN)) != NULL) + __bt_dpage(h); + (void)fflush(tracefp); + return (0); +} + +/* + * BT_DPAGE -- Dump the page + * + * Parameters: + * h: pointer to the PAGE + */ +int +__bt_dpage(h) + PAGE *h; +{ + BINTERNAL *bi; + BLEAF *bl; + RINTERNAL *ri; + RLEAF *rl; + indx_t cur, top; + char *sep; + + __bt_dinit(); + + (void)fprintf(tracefp, " page %d: (", h->pgno); +#undef X +#define X(flag, name) \ + if (h->flags & flag) { \ + (void)fprintf(tracefp, "%s%s", sep, name); \ + sep = ", "; \ + } + sep = ""; + X(P_BINTERNAL, "BINTERNAL") /* types */ + X(P_BLEAF, "BLEAF") + X(P_RINTERNAL, "RINTERNAL") /* types */ + X(P_RLEAF, "RLEAF") + X(P_OVERFLOW, "OVERFLOW") + X(P_PRESERVE, "PRESERVE"); + (void)fprintf(tracefp, ")\n"); +#undef X + + (void)fprintf(tracefp, "\tprev %2d next %2d", h->prevpg, h->nextpg); + if (h->flags & P_OVERFLOW) + return; + + top = NEXTINDEX(h); + (void)fprintf(tracefp, " lower %3d upper %3d nextind %d\n", + h->lower, h->upper, top); + for (cur = 0; cur < top; cur++) { + (void)fprintf(tracefp, "\t[%03d] %4d ", cur, h->linp[cur]); + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + bi = GETBINTERNAL(h, cur); + (void)fprintf(tracefp, + "size %03d pgno %03d", bi->ksize, bi->pgno); + if (bi->flags & P_BIGKEY) + (void)fprintf(tracefp, " (indirect)"); + else if (bi->ksize) + (void)fprintf(tracefp, + " {%.*s}", (int)bi->ksize, bi->bytes); + break; + case P_RINTERNAL: + ri = GETRINTERNAL(h, cur); + (void)fprintf(tracefp, "entries %03d pgno %03d", + ri->nrecs, ri->pgno); + break; + case P_BLEAF: + bl = GETBLEAF(h, cur); + if (bl->flags & P_BIGKEY) + (void)fprintf(tracefp, + "big key page %lu size %u/", + *(db_pgno_t *)bl->bytes, + *(u_int32_t *)(bl->bytes + sizeof(db_pgno_t))); + else if (bl->ksize) + (void)fprintf(tracefp, "%s/", bl->bytes); + if (bl->flags & P_BIGDATA) + (void)fprintf(tracefp, + "big data page %lu size %u", + *(db_pgno_t *)(bl->bytes + bl->ksize), + *(u_int32_t *)(bl->bytes + bl->ksize + + sizeof(db_pgno_t))); + else if (bl->dsize) + (void)fprintf(tracefp, "%.*s", + (int)bl->dsize, bl->bytes + bl->ksize); + break; + case P_RLEAF: + rl = GETRLEAF(h, cur); + if (rl->flags & P_BIGDATA) + (void)fprintf(tracefp, + "big data page %lu size %u", + *(db_pgno_t *)rl->bytes, + *(u_int32_t *)(rl->bytes + sizeof(db_pgno_t))); + else if (rl->dsize) + (void)fprintf(tracefp, + "%.*s", (int)rl->dsize, rl->bytes); + break; + } + (void)fprintf(tracefp, "\n"); + } + (void)fflush(tracefp); + return (0); +} +#endif + +#ifdef STATISTICS +/* + * bt_stat -- + * Gather/print the tree statistics + * + * Parameters: + * dbp: pointer to the DB + */ +int +__bt_stat(dbp) + DB *dbp; +{ + extern u_long bt_cache_hit, bt_cache_miss, bt_pfxsaved, bt_rootsplit; + extern u_long bt_sortsplit, bt_split; + BTREE *t; + PAGE *h; + db_pgno_t i, pcont, pinternal, pleaf; + u_long ifree, lfree, nkeys; + int levels; + + __bt_dinit(); + + t = dbp->internal; + pcont = pinternal = pleaf = 0; + nkeys = ifree = lfree = 0; + for (i = P_ROOT; + (h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN)) != NULL; ++i) + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + case P_RINTERNAL: + ++pinternal; + ifree += h->upper - h->lower; + break; + case P_BLEAF: + case P_RLEAF: + ++pleaf; + lfree += h->upper - h->lower; + nkeys += NEXTINDEX(h); + break; + case P_OVERFLOW: + ++pcont; + break; + } + + /* Count the levels of the tree. */ + for (i = P_ROOT, levels = 0 ;; ++levels) { + h = mpool_get(t->bt_mp, i, MPOOL_IGNOREPIN); + if (h->flags & (P_BLEAF|P_RLEAF)) { + if (levels == 0) + levels = 1; + break; + } + i = F_ISSET(t, R_RECNO) ? + GETRINTERNAL(h, 0)->pgno : + GETBINTERNAL(h, 0)->pgno; + } + + (void)fprintf(tracefp, "%d level%s with %ld keys", + levels, levels == 1 ? "" : "s", nkeys); + if (F_ISSET(t, R_RECNO)) + (void)fprintf(tracefp, " (%ld header count)", t->bt_nrecs); + (void)fprintf(tracefp, + "\n%lu pages (leaf %ld, internal %ld, overflow %ld)\n", + pinternal + pleaf + pcont, pleaf, pinternal, pcont); + (void)fprintf(tracefp, "%ld cache hits, %ld cache misses\n", + bt_cache_hit, bt_cache_miss); + (void)fprintf(tracefp, + "%ld splits (%ld root splits, %ld sort splits)\n", + bt_split, bt_rootsplit, bt_sortsplit); + pleaf *= t->bt_psize - BTDATAOFF; + if (pleaf) + (void)fprintf(tracefp, + "%.0f%% leaf fill (%ld bytes used, %ld bytes free)\n", + ((double)(pleaf - lfree) / pleaf) * 100, + pleaf - lfree, lfree); + pinternal *= t->bt_psize - BTDATAOFF; + if (pinternal) + (void)fprintf(tracefp, + "%.0f%% internal fill (%ld bytes used, %ld bytes free\n", + ((double)(pinternal - ifree) / pinternal) * 100, + pinternal - ifree, ifree); + if (bt_pfxsaved) + (void)fprintf(tracefp, "prefix checking removed %lu bytes.\n", + bt_pfxsaved); + (void)fflush(tracefp); + return (0); +} +#endif diff --git a/src/util/db2/btree/bt_delete.c b/src/util/db2/btree/bt_delete.c new file mode 100644 index 000000000..078981c58 --- /dev/null +++ b/src/util/db2/btree/bt_delete.c @@ -0,0 +1,657 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_delete.c 8.13 (Berkeley) 7/28/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +static int __bt_bdelete __P((BTREE *, const DBT *)); +static int __bt_curdel __P((BTREE *, const DBT *, PAGE *, u_int)); +static int __bt_pdelete __P((BTREE *, PAGE *)); +static int __bt_relink __P((BTREE *, PAGE *)); +static int __bt_stkacq __P((BTREE *, PAGE **, CURSOR *)); + +/* + * __bt_delete + * Delete the item(s) referenced by a key. + * + * Return RET_SPECIAL if the key is not found. + */ +int +__bt_delete(dbp, key, flags) + const DB *dbp; + const DBT *key; + u_int flags; +{ + BTREE *t; + CURSOR *c; + PAGE *h; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Check for change to a read-only tree. */ + if (F_ISSET(t, B_RDONLY)) { + errno = EPERM; + return (RET_ERROR); + } + + switch (flags) { + case 0: + status = __bt_bdelete(t, key); + break; + case R_CURSOR: + /* + * If flags is R_CURSOR, delete the cursor. Must already + * have started a scan and not have already deleted it. + */ + c = &t->bt_cursor; + if (F_ISSET(c, CURS_INIT)) { + if (F_ISSET(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE)) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) + return (RET_ERROR); + + /* + * If the page is about to be emptied, we'll need to + * delete it, which means we have to acquire a stack. + */ + if (NEXTINDEX(h) == 1) + if (__bt_stkacq(t, &h, &t->bt_cursor)) + return (RET_ERROR); + + status = __bt_dleaf(t, NULL, h, c->pg.index); + + if (NEXTINDEX(h) == 0 && status == RET_SUCCESS) { + if (__bt_pdelete(t, h)) + return (RET_ERROR); + } else + mpool_put(t->bt_mp, + h, status == RET_SUCCESS ? MPOOL_DIRTY : 0); + break; + } + /* FALLTHROUGH */ + default: + errno = EINVAL; + return (RET_ERROR); + } + if (status == RET_SUCCESS) + F_SET(t, B_MODIFIED); + return (status); +} + +/* + * __bt_stkacq -- + * Acquire a stack so we can delete a cursor entry. + * + * Parameters: + * t: tree + * hp: pointer to current, pinned PAGE pointer + * c: pointer to the cursor + * + * Returns: + * 0 on success, 1 on failure + */ +static int +__bt_stkacq(t, hp, c) + BTREE *t; + PAGE **hp; + CURSOR *c; +{ + BINTERNAL *bi; + EPG *e; + EPGNO *parent; + PAGE *h; + indx_t index; + db_pgno_t pgno; + recno_t nextpg, prevpg; + int exact, level; + + /* + * Find the first occurrence of the key in the tree. Toss the + * currently locked page so we don't hit an already-locked page. + */ + h = *hp; + mpool_put(t->bt_mp, h, 0); + if ((e = __bt_search(t, &c->key, &exact)) == NULL) + return (1); + h = e->page; + + /* See if we got it in one shot. */ + if (h->pgno == c->pg.pgno) + goto ret; + + /* + * Move right, looking for the page. At each move we have to move + * up the stack until we don't have to move to the next page. If + * we have to change pages at an internal level, we have to fix the + * stack back up. + */ + while (h->pgno != c->pg.pgno) { + if ((nextpg = h->nextpg) == P_INVALID) + break; + mpool_put(t->bt_mp, h, 0); + + /* Move up the stack. */ + for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + return (1); + + /* Move to the next index. */ + if (parent->index != NEXTINDEX(h) - 1) { + index = parent->index + 1; + BT_PUSH(t, h->pgno, index); + break; + } + mpool_put(t->bt_mp, h, 0); + } + + /* Restore the stack. */ + while (level--) { + /* Push the next level down onto the stack. */ + bi = GETBINTERNAL(h, index); + pgno = bi->pgno; + BT_PUSH(t, pgno, 0); + + /* Lose the currently pinned page. */ + mpool_put(t->bt_mp, h, 0); + + /* Get the next level down. */ + if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) + return (1); + index = 0; + } + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, nextpg, 0)) == NULL) + return (1); + } + + if (h->pgno == c->pg.pgno) + goto ret; + + /* Reacquire the original stack. */ + mpool_put(t->bt_mp, h, 0); + if ((e = __bt_search(t, &c->key, &exact)) == NULL) + return (1); + h = e->page; + + /* + * Move left, looking for the page. At each move we have to move + * up the stack until we don't have to change pages to move to the + * next page. If we have to change pages at an internal level, we + * have to fix the stack back up. + */ + while (h->pgno != c->pg.pgno) { + if ((prevpg = h->prevpg) == P_INVALID) + break; + mpool_put(t->bt_mp, h, 0); + + /* Move up the stack. */ + for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + return (1); + + /* Move to the next index. */ + if (parent->index != 0) { + index = parent->index - 1; + BT_PUSH(t, h->pgno, index); + break; + } + mpool_put(t->bt_mp, h, 0); + } + + /* Restore the stack. */ + while (level--) { + /* Push the next level down onto the stack. */ + bi = GETBINTERNAL(h, index); + pgno = bi->pgno; + + /* Lose the currently pinned page. */ + mpool_put(t->bt_mp, h, 0); + + /* Get the next level down. */ + if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) + return (1); + + index = NEXTINDEX(h) - 1; + BT_PUSH(t, pgno, index); + } + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, prevpg, 0)) == NULL) + return (1); + } + + +ret: mpool_put(t->bt_mp, h, 0); + return ((*hp = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL); +} + +/* + * __bt_bdelete -- + * Delete all key/data pairs matching the specified key. + * + * Parameters: + * t: tree + * key: key to delete + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +static int +__bt_bdelete(t, key) + BTREE *t; + const DBT *key; +{ + EPG *e; + PAGE *h; + int deleted, exact, redo; + + deleted = 0; + + /* Find any matching record; __bt_search pins the page. */ +loop: if ((e = __bt_search(t, key, &exact)) == NULL) + return (deleted ? RET_SUCCESS : RET_ERROR); + if (!exact) { + mpool_put(t->bt_mp, e->page, 0); + return (deleted ? RET_SUCCESS : RET_SPECIAL); + } + + /* + * Delete forward, then delete backward, from the found key. If + * there are duplicates and we reach either side of the page, do + * the key search again, so that we get them all. + */ + redo = 0; + h = e->page; + do { + if (__bt_dleaf(t, key, h, e->index)) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + if (F_ISSET(t, B_NODUPS)) { + if (NEXTINDEX(h) == 0) { + if (__bt_pdelete(t, h)) + return (RET_ERROR); + } else + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); + } + deleted = 1; + } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0); + + /* Check for right-hand edge of the page. */ + if (e->index == NEXTINDEX(h)) + redo = 1; + + /* Delete from the key to the beginning of the page. */ + while (e->index-- > 0) { + if (__bt_cmp(t, key, e) != 0) + break; + if (__bt_dleaf(t, key, h, e->index) == RET_ERROR) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + if (e->index == 0) + redo = 1; + } + + /* Check for an empty page. */ + if (NEXTINDEX(h) == 0) { + if (__bt_pdelete(t, h)) + return (RET_ERROR); + goto loop; + } + + /* Put the page. */ + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + if (redo) + goto loop; + return (RET_SUCCESS); +} + +/* + * __bt_pdelete -- + * Delete a single page from the tree. + * + * Parameters: + * t: tree + * h: leaf page + * + * Returns: + * RET_SUCCESS, RET_ERROR. + * + * Side-effects: + * mpool_put's the page + */ +static int +__bt_pdelete(t, h) + BTREE *t; + PAGE *h; +{ + BINTERNAL *bi; + PAGE *pg; + EPGNO *parent; + indx_t cnt, index, *ip, offset; + u_int32_t nksize; + char *from; + + /* + * Walk the parent page stack -- a LIFO stack of the pages that were + * traversed when we searched for the page where the delete occurred. + * Each stack entry is a page number and a page index offset. The + * offset is for the page traversed on the search. We've just deleted + * a page, so we have to delete the key from the parent page. + * + * If the delete from the parent page makes it empty, this process may + * continue all the way up the tree. We stop if we reach the root page + * (which is never deleted, it's just not worth the effort) or if the + * delete does not empty the page. + */ + while ((parent = BT_POP(t)) != NULL) { + /* Get the parent page. */ + if ((pg = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + return (RET_ERROR); + + index = parent->index; + bi = GETBINTERNAL(pg, index); + + /* Free any overflow pages. */ + if (bi->flags & P_BIGKEY && + __ovfl_delete(t, bi->bytes) == RET_ERROR) { + mpool_put(t->bt_mp, pg, 0); + return (RET_ERROR); + } + + /* + * Free the parent if it has only the one key and it's not the + * root page. If it's the rootpage, turn it back into an empty + * leaf page. + */ + if (NEXTINDEX(pg) == 1) + if (pg->pgno == P_ROOT) { + pg->lower = BTDATAOFF; + pg->upper = t->bt_psize; + pg->flags = P_BLEAF; + } else { + if (__bt_relink(t, pg) || __bt_free(t, pg)) + return (RET_ERROR); + continue; + } + else { + /* Pack remaining key items at the end of the page. */ + nksize = NBINTERNAL(bi->ksize); + from = (char *)pg + pg->upper; + memmove(from + nksize, from, (char *)bi - from); + pg->upper += nksize; + + /* Adjust indices' offsets, shift the indices down. */ + offset = pg->linp[index]; + for (cnt = index, ip = &pg->linp[0]; cnt--; ++ip) + if (ip[0] < offset) + ip[0] += nksize; + for (cnt = NEXTINDEX(pg) - index; --cnt; ++ip) + ip[0] = ip[1] < offset ? ip[1] + nksize : ip[1]; + pg->lower -= sizeof(indx_t); + } + + mpool_put(t->bt_mp, pg, MPOOL_DIRTY); + break; + } + + /* Free the leaf page, as long as it wasn't the root. */ + if (h->pgno == P_ROOT) { + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); + } + return (__bt_relink(t, h) || __bt_free(t, h)); +} + +/* + * __bt_dleaf -- + * Delete a single record from a leaf page. + * + * Parameters: + * t: tree + * key: referenced key + * h: page + * index: index on page to delete + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_dleaf(t, key, h, index) + BTREE *t; + const DBT *key; + PAGE *h; + u_int index; +{ + BLEAF *bl; + indx_t cnt, *ip, offset; + u_int32_t nbytes; + void *to; + char *from; + + /* If this record is referenced by the cursor, delete the cursor. */ + if (F_ISSET(&t->bt_cursor, CURS_INIT) && + !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && + t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index == index && + __bt_curdel(t, key, h, index)) + return (RET_ERROR); + + /* If the entry uses overflow pages, make them available for reuse. */ + to = bl = GETBLEAF(h, index); + if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR) + return (RET_ERROR); + if (bl->flags & P_BIGDATA && + __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR) + return (RET_ERROR); + + /* Pack the remaining key/data items at the end of the page. */ + nbytes = NBLEAF(bl); + from = (char *)h + h->upper; + memmove(from + nbytes, from, (char *)to - from); + h->upper += nbytes; + + /* Adjust the indices' offsets, shift the indices down. */ + offset = h->linp[index]; + for (cnt = index, ip = &h->linp[0]; cnt--; ++ip) + if (ip[0] < offset) + ip[0] += nbytes; + for (cnt = NEXTINDEX(h) - index; --cnt; ++ip) + ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; + h->lower -= sizeof(indx_t); + + /* If the cursor is on this page, adjust it as necessary. */ + if (F_ISSET(&t->bt_cursor, CURS_INIT) && + !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && + t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index > index) + --t->bt_cursor.pg.index; + + return (RET_SUCCESS); +} + +/* + * __bt_curdel -- + * Delete the cursor. + * + * Parameters: + * t: tree + * key: referenced key (or NULL) + * h: page + * index: index on page to delete + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +static int +__bt_curdel(t, key, h, index) + BTREE *t; + const DBT *key; + PAGE *h; + u_int index; +{ + CURSOR *c; + EPG e; + PAGE *pg; + int curcopy, status; + + /* + * If there are duplicates, move forward or backward to one. + * Otherwise, copy the key into the cursor area. + */ + c = &t->bt_cursor; + F_CLR(c, CURS_AFTER | CURS_BEFORE | CURS_ACQUIRE); + + curcopy = 0; + if (!F_ISSET(t, B_NODUPS)) { + /* + * We're going to have to do comparisons. If we weren't + * provided a copy of the key, i.e. the user is deleting + * the current cursor position, get one. + */ + if (key == NULL) { + e.page = h; + e.index = index; + if ((status = __bt_ret(t, &e, + &c->key, &c->key, NULL, NULL, 1)) != RET_SUCCESS) + return (status); + curcopy = 1; + key = &c->key; + } + /* Check previous key, if not at the beginning of the page. */ + if (index > 0) { + e.page = h; + e.index = index - 1; + if (__bt_cmp(t, key, &e) == 0) { + F_SET(c, CURS_BEFORE); + goto dup2; + } + } + /* Check next key, if not at the end of the page. */ + if (index < NEXTINDEX(h) - 1) { + e.page = h; + e.index = index + 1; + if (__bt_cmp(t, key, &e) == 0) { + F_SET(c, CURS_AFTER); + goto dup2; + } + } + /* Check previous key if at the beginning of the page. */ + if (index == 0 && h->prevpg != P_INVALID) { + if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) + return (RET_ERROR); + e.page = pg; + e.index = NEXTINDEX(pg) - 1; + if (__bt_cmp(t, key, &e) == 0) { + F_SET(c, CURS_BEFORE); + goto dup1; + } + mpool_put(t->bt_mp, pg, 0); + } + /* Check next key if at the end of the page. */ + if (index == NEXTINDEX(h) - 1 && h->nextpg != P_INVALID) { + if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) + return (RET_ERROR); + e.page = pg; + e.index = 0; + if (__bt_cmp(t, key, &e) == 0) { + F_SET(c, CURS_AFTER); +dup1: mpool_put(t->bt_mp, pg, 0); +dup2: c->pg.pgno = e.page->pgno; + c->pg.index = e.index; + return (RET_SUCCESS); + } + mpool_put(t->bt_mp, pg, 0); + } + } + e.page = h; + e.index = index; + if (curcopy || (status = + __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) == RET_SUCCESS) { + F_SET(c, CURS_ACQUIRE); + return (RET_SUCCESS); + } + return (status); +} + +/* + * __bt_relink -- + * Link around a deleted page. + * + * Parameters: + * t: tree + * h: page to be deleted + */ +static int +__bt_relink(t, h) + BTREE *t; + PAGE *h; +{ + PAGE *pg; + + if (h->nextpg != P_INVALID) { + if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) + return (RET_ERROR); + pg->prevpg = h->prevpg; + mpool_put(t->bt_mp, pg, MPOOL_DIRTY); + } + if (h->prevpg != P_INVALID) { + if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) + return (RET_ERROR); + pg->nextpg = h->nextpg; + mpool_put(t->bt_mp, pg, MPOOL_DIRTY); + } + return (0); +} diff --git a/src/util/db2/btree/bt_get.c b/src/util/db2/btree/bt_get.c new file mode 100644 index 000000000..b6318211a --- /dev/null +++ b/src/util/db2/btree/bt_get.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_get.c 8.6 (Berkeley) 7/20/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +/* + * __BT_GET -- Get a record from the btree. + * + * Parameters: + * dbp: pointer to access method + * key: key to find + * data: data to return + * flag: currently unused + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__bt_get(dbp, key, data, flags) + const DB *dbp; + const DBT *key; + DBT *data; + u_int flags; +{ + BTREE *t; + EPG *e; + int exact, status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Get currently doesn't take any flags. */ + if (flags) { + errno = EINVAL; + return (RET_ERROR); + } + + if ((e = __bt_search(t, key, &exact)) == NULL) + return (RET_ERROR); + if (!exact) { + mpool_put(t->bt_mp, e->page, 0); + return (RET_SPECIAL); + } + + status = __bt_ret(t, e, NULL, NULL, data, &t->bt_rdata, 0); + + /* + * If the user is doing concurrent access, we copied the + * key/data, toss the page. + */ + if (F_ISSET(t, B_DB_LOCK)) + mpool_put(t->bt_mp, e->page, 0); + else + t->bt_pinned = e->page; + return (status); +} diff --git a/src/util/db2/btree/bt_open.c b/src/util/db2/btree/bt_open.c new file mode 100644 index 000000000..3a7aa9462 --- /dev/null +++ b/src/util/db2/btree/bt_open.c @@ -0,0 +1,471 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_open.c 8.11 (Berkeley) 11/2/95"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Implementation of btree access method for 4.4BSD. + * + * The design here was originally based on that of the btree access method + * used in the Postgres database system at UC Berkeley. This implementation + * is wholly independent of the Postgres code. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +#ifdef DEBUG +#undef MINPSIZE +#define MINPSIZE 128 +#endif + +static int byteorder __P((void)); +static int nroot __P((BTREE *)); +static int tmp __P((void)); + +/* + * __BT_OPEN -- Open a btree. + * + * Creates and fills a DB struct, and calls the routine that actually + * opens the btree. + * + * Parameters: + * fname: filename (NULL for in-memory trees) + * flags: open flag bits + * mode: open permission bits + * b: BTREEINFO pointer + * + * Returns: + * NULL on failure, pointer to DB on success. + * + */ +DB * +__bt_open(fname, flags, mode, openinfo, dflags) + const char *fname; + int flags, mode, dflags; + const BTREEINFO *openinfo; +{ + struct stat sb; + BTMETA m; + BTREE *t; + BTREEINFO b; + DB *dbp; + db_pgno_t ncache; + ssize_t nr; + int machine_lorder; + + t = NULL; + + /* + * Intention is to make sure all of the user's selections are okay + * here and then use them without checking. Can't be complete, since + * we don't know the right page size, lorder or flags until the backing + * file is opened. Also, the file's page size can cause the cachesize + * to change. + */ + machine_lorder = byteorder(); + if (openinfo) { + b = *openinfo; + + /* Flags: R_DUP. */ + if (b.flags & ~(R_DUP)) + goto einval; + + /* + * Page size must be indx_t aligned and >= MINPSIZE. Default + * page size is set farther on, based on the underlying file + * transfer size. + */ + if (b.psize && + (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 || + b.psize & sizeof(indx_t) - 1)) + goto einval; + + /* Minimum number of keys per page; absolute minimum is 2. */ + if (b.minkeypage) { + if (b.minkeypage < 2) + goto einval; + } else + b.minkeypage = DEFMINKEYPAGE; + + /* If no comparison, use default comparison and prefix. */ + if (b.compare == NULL) { + b.compare = __bt_defcmp; + if (b.prefix == NULL) + b.prefix = __bt_defpfx; + } + + if (b.lorder == 0) + b.lorder = machine_lorder; + } else { + b.compare = __bt_defcmp; + b.cachesize = 0; + b.flags = 0; + b.lorder = machine_lorder; + b.minkeypage = DEFMINKEYPAGE; + b.prefix = __bt_defpfx; + b.psize = 0; + } + + /* Check for the ubiquitous PDP-11. */ + if (b.lorder != DB_BIG_ENDIAN && b.lorder != DB_LITTLE_ENDIAN) + goto einval; + + /* Allocate and initialize DB and BTREE structures. */ + if ((t = (BTREE *)malloc(sizeof(BTREE))) == NULL) + goto err; + memset(t, 0, sizeof(BTREE)); + t->bt_fd = -1; /* Don't close unopened fd on error. */ + t->bt_lorder = b.lorder; + t->bt_order = NOT; + t->bt_cmp = b.compare; + t->bt_pfx = b.prefix; + t->bt_rfd = -1; + + if ((t->bt_dbp = dbp = (DB *)malloc(sizeof(DB))) == NULL) + goto err; + memset(t->bt_dbp, 0, sizeof(DB)); + if (t->bt_lorder != machine_lorder) + F_SET(t, B_NEEDSWAP); + + dbp->type = DB_BTREE; + dbp->internal = t; + dbp->close = __bt_close; + dbp->del = __bt_delete; + dbp->fd = __bt_fd; + dbp->get = __bt_get; + dbp->put = __bt_put; + dbp->seq = __bt_seq; + dbp->sync = __bt_sync; + + /* + * If no file name was supplied, this is an in-memory btree and we + * open a backing temporary file. Otherwise, it's a disk-based tree. + */ + if (fname) { + switch (flags & O_ACCMODE) { + case O_RDONLY: + F_SET(t, B_RDONLY); + break; + case O_RDWR: + break; + case O_WRONLY: + default: + goto einval; + } + + if ((t->bt_fd = open(fname, flags, mode)) < 0) + goto err; + + } else { + if ((flags & O_ACCMODE) != O_RDWR) + goto einval; + if ((t->bt_fd = tmp()) == -1) + goto err; + F_SET(t, B_INMEM); + } + + if (fcntl(t->bt_fd, F_SETFD, 1) == -1) + goto err; + + if (fstat(t->bt_fd, &sb)) + goto err; + if (sb.st_size) { + if ((nr = read(t->bt_fd, &m, sizeof(BTMETA))) < 0) + goto err; + if (nr != sizeof(BTMETA)) + goto eftype; + + /* + * Read in the meta-data. This can change the notion of what + * the lorder, page size and flags are, and, when the page size + * changes, the cachesize value can change too. If the user + * specified the wrong byte order for an existing database, we + * don't bother to return an error, we just clear the NEEDSWAP + * bit. + */ + if (m.magic == BTREEMAGIC) + F_CLR(t, B_NEEDSWAP); + else { + F_SET(t, B_NEEDSWAP); + M_32_SWAP(m.magic); + M_32_SWAP(m.version); + M_32_SWAP(m.psize); + M_32_SWAP(m.free); + M_32_SWAP(m.nrecs); + M_32_SWAP(m.flags); + } + if (m.magic != BTREEMAGIC || m.version != BTREEVERSION) + goto eftype; + if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 || + m.psize & sizeof(indx_t) - 1) + goto eftype; + if (m.flags & ~SAVEMETA) + goto eftype; + b.psize = m.psize; + F_SET(t, m.flags); + t->bt_free = m.free; + t->bt_nrecs = m.nrecs; + } else { + /* + * Set the page size to the best value for I/O to this file. + * Don't overflow the page offset type. + */ + if (b.psize == 0) { + b.psize = sb.st_blksize; + if (b.psize < MINPSIZE) + b.psize = MINPSIZE; + if (b.psize > MAX_PAGE_OFFSET + 1) + b.psize = MAX_PAGE_OFFSET + 1; + } + + /* Set flag if duplicates permitted. */ + if (!(b.flags & R_DUP)) + F_SET(t, B_NODUPS); + + t->bt_free = P_INVALID; + t->bt_nrecs = 0; + F_SET(t, B_METADIRTY); + } + + t->bt_psize = b.psize; + + /* Set the cache size; must be a multiple of the page size. */ + if (b.cachesize && b.cachesize & b.psize - 1) + b.cachesize += (~b.cachesize & b.psize - 1) + 1; + if (b.cachesize < b.psize * MINCACHE) + b.cachesize = b.psize * MINCACHE; + + /* Calculate number of pages to cache. */ + ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize; + + /* + * The btree data structure requires that at least two keys can fit on + * a page, but other than that there's no fixed requirement. The user + * specified a minimum number per page, and we translated that into the + * number of bytes a key/data pair can use before being placed on an + * overflow page. This calculation includes the page header, the size + * of the index referencing the leaf item and the size of the leaf item + * structure. Also, don't let the user specify a minkeypage such that + * a key/data pair won't fit even if both key and data are on overflow + * pages. + */ + t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage - + (sizeof(indx_t) + NBLEAFDBT(0, 0)); + if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t)) + t->bt_ovflsize = + NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t); + + /* Initialize the buffer pool. */ + if ((t->bt_mp = + mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL) + goto err; + if (!F_ISSET(t, B_INMEM)) + mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t); + + /* Create a root page if new tree. */ + if (nroot(t) == RET_ERROR) + goto err; + + /* Global flags. */ + if (dflags & DB_LOCK) + F_SET(t, B_DB_LOCK); + if (dflags & DB_SHMEM) + F_SET(t, B_DB_SHMEM); + if (dflags & DB_TXN) + F_SET(t, B_DB_TXN); + + return (dbp); + +einval: errno = EINVAL; + goto err; + +eftype: errno = EFTYPE; + goto err; + +err: if (t) { + if (t->bt_dbp) + free(t->bt_dbp); + if (t->bt_fd != -1) + (void)close(t->bt_fd); + free(t); + } + return (NULL); +} + +/* + * NROOT -- Create the root of a new tree. + * + * Parameters: + * t: tree + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +nroot(t) + BTREE *t; +{ + PAGE *meta, *root; + db_pgno_t npg; + + if ((root = mpool_get(t->bt_mp, 1, 0)) != NULL) { + if (root->lower == 0 && + root->pgno == 0 && + root->linp[0] == 0) { + mpool_delete(t->bt_mp, root); + errno = EINVAL; + } else { + mpool_put(t->bt_mp, root, 0); + return (RET_SUCCESS); + } + } + if (errno != EINVAL) /* It's OK to not exist. */ + return (RET_ERROR); + errno = 0; + + if ((meta = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) + return (RET_ERROR); + + if ((root = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) + return (RET_ERROR); + + if (npg != P_ROOT) + return (RET_ERROR); + root->pgno = npg; + root->prevpg = root->nextpg = P_INVALID; + root->lower = BTDATAOFF; + root->upper = t->bt_psize; + root->flags = P_BLEAF; + memset(meta, 0, t->bt_psize); + mpool_put(t->bt_mp, meta, MPOOL_DIRTY); + mpool_put(t->bt_mp, root, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +static int +tmp() +{ +#ifdef SIG_BLOCK + sigset_t set, oset; +#else + int oset; +#endif + int fd; + char *envtmp; + char path[MAXPATHLEN]; + static char fn[] = "/bt.XXXXXX"; + + envtmp = getenv("TMPDIR"); + + /* this used to be done with snprintf(), but since snprintf + isn't in most operating systems, and overflow checking in + this case is easy, this is what is done */ + + if (envtmp && ((strlen(envtmp)+sizeof(fn)+1) > sizeof(path))) + return(-1); + + (void)sprintf(path, "%s%s", (envtmp ? envtmp : "/tmp"), fn); + +#ifdef SIG_BLOCK + (void)sigfillset(&set); + (void)sigprocmask(SIG_BLOCK, &set, &oset); +#else + oset = sigblock(~0); +#endif + if ((fd = mkstemp(path)) != -1) + (void)unlink(path); +#ifdef SIG_BLOCK + (void)sigprocmask(SIG_SETMASK, &oset, NULL); +#else + sigsetmask(oset); +#endif + return(fd); +} + +static int +byteorder() +{ + u_int32_t x; + u_char *p; + + x = 0x01020304; + p = (u_char *)&x; + switch (*p) { + case 1: + return (DB_BIG_ENDIAN); + case 4: + return (DB_LITTLE_ENDIAN); + default: + return (0); + } +} + +int +__bt_fd(dbp) + const DB *dbp; +{ + BTREE *t; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* In-memory database can't have a file descriptor. */ + if (F_ISSET(t, B_INMEM)) { + errno = ENOENT; + return (-1); + } + return (t->bt_fd); +} diff --git a/src/util/db2/btree/bt_overflow.c b/src/util/db2/btree/bt_overflow.c new file mode 100644 index 000000000..8b1f59791 --- /dev/null +++ b/src/util/db2/btree/bt_overflow.c @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_overflow.c 8.5 (Berkeley) 7/16/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +/* + * Big key/data code. + * + * Big key and data entries are stored on linked lists of pages. The initial + * reference is byte string stored with the key or data and is the page number + * and size. The actual record is stored in a chain of pages linked by the + * nextpg field of the PAGE header. + * + * The first page of the chain has a special property. If the record is used + * by an internal page, it cannot be deleted and the P_PRESERVE bit will be set + * in the header. + * + * XXX + * A single DBT is written to each chain, so a lot of space on the last page + * is wasted. This is a fairly major bug for some data sets. + */ + +/* + * __OVFL_GET -- Get an overflow key/data item. + * + * Parameters: + * t: tree + * p: pointer to { db_pgno_t, u_int32_t } + * buf: storage address + * bufsz: storage size + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_get(t, p, ssz, buf, bufsz) + BTREE *t; + void *p; + size_t *ssz; + void **buf; + size_t *bufsz; +{ + PAGE *h; + db_pgno_t pg; + size_t nb, plen; + u_int32_t sz; + + memmove(&pg, p, sizeof(db_pgno_t)); + memmove(&sz, (char *)p + sizeof(db_pgno_t), sizeof(u_int32_t)); + *ssz = sz; + +#ifdef DEBUG + if (pg == P_INVALID || sz == 0) + abort(); +#endif + /* Make the buffer bigger as necessary. */ + if (*bufsz < sz) { + *buf = (char *)(*buf == NULL ? malloc(sz) : realloc(*buf, sz)); + if (*buf == NULL) + return (RET_ERROR); + *bufsz = sz; + } + + /* + * Step through the linked list of pages, copying the data on each one + * into the buffer. Never copy more than the data's length. + */ + plen = t->bt_psize - BTDATAOFF; + for (p = *buf;; p = (char *)p + nb, pg = h->nextpg) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + nb = MIN(sz, plen); + memmove(p, (char *)h + BTDATAOFF, nb); + mpool_put(t->bt_mp, h, 0); + + if ((sz -= nb) == 0) + break; + } + return (RET_SUCCESS); +} + +/* + * __OVFL_PUT -- Store an overflow key/data item. + * + * Parameters: + * t: tree + * data: DBT to store + * pgno: storage page number + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_put(t, dbt, pg) + BTREE *t; + const DBT *dbt; + db_pgno_t *pg; +{ + PAGE *h, *last; + void *p; + db_pgno_t npg; + size_t nb, plen; + u_int32_t sz; + + /* + * Allocate pages and copy the key/data record into them. Store the + * number of the first page in the chain. + */ + plen = t->bt_psize - BTDATAOFF; + for (last = NULL, p = dbt->data, sz = dbt->size;; + p = (char *)p + plen, last = h) { + if ((h = __bt_new(t, &npg)) == NULL) + return (RET_ERROR); + + h->pgno = npg; + h->nextpg = h->prevpg = P_INVALID; + h->flags = P_OVERFLOW; + h->lower = h->upper = 0; + + nb = MIN(sz, plen); + memmove((char *)h + BTDATAOFF, p, nb); + + if (last) { + last->nextpg = h->pgno; + mpool_put(t->bt_mp, last, MPOOL_DIRTY); + } else + *pg = h->pgno; + + if ((sz -= nb) == 0) { + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + } + } + return (RET_SUCCESS); +} + +/* + * __OVFL_DELETE -- Delete an overflow chain. + * + * Parameters: + * t: tree + * p: pointer to { db_pgno_t, u_int32_t } + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__ovfl_delete(t, p) + BTREE *t; + void *p; +{ + PAGE *h; + db_pgno_t pg; + size_t plen; + u_int32_t sz; + + memmove(&pg, p, sizeof(db_pgno_t)); + memmove(&sz, (char *)p + sizeof(db_pgno_t), sizeof(u_int32_t)); + +#ifdef DEBUG + if (pg == P_INVALID || sz == 0) + abort(); +#endif + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + /* Don't delete chains used by internal pages. */ + if (h->flags & P_PRESERVE) { + mpool_put(t->bt_mp, h, 0); + return (RET_SUCCESS); + } + + /* Step through the chain, calling the free routine for each page. */ + for (plen = t->bt_psize - BTDATAOFF;; sz -= plen) { + pg = h->nextpg; + __bt_free(t, h); + if (sz <= plen) + break; + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + } + return (RET_SUCCESS); +} diff --git a/src/util/db2/btree/bt_page.c b/src/util/db2/btree/bt_page.c new file mode 100644 index 000000000..cb65040a7 --- /dev/null +++ b/src/util/db2/btree/bt_page.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_page.c 8.4 (Berkeley) 11/2/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +#include "db-int.h" +#include "btree.h" + +/* + * __bt_free -- + * Put a page on the freelist. + * + * Parameters: + * t: tree + * h: page to free + * + * Returns: + * RET_ERROR, RET_SUCCESS + * + * Side-effect: + * mpool_put's the page. + */ +int +__bt_free(t, h) + BTREE *t; + PAGE *h; +{ + /* Insert the page at the head of the free list. */ + h->prevpg = P_INVALID; + h->nextpg = t->bt_free; + t->bt_free = h->pgno; + + /* Make sure the page gets written back. */ + return (mpool_put(t->bt_mp, h, MPOOL_DIRTY)); +} + +/* + * __bt_new -- + * Get a new page, preferably from the freelist. + * + * Parameters: + * t: tree + * npg: storage for page number. + * + * Returns: + * Pointer to a page, NULL on error. + */ +PAGE * +__bt_new(t, npg) + BTREE *t; + db_pgno_t *npg; +{ + PAGE *h; + + if (t->bt_free != P_INVALID && + (h = mpool_get(t->bt_mp, t->bt_free, 0)) != NULL) { + *npg = t->bt_free; + t->bt_free = h->nextpg; + return (h); + } + return (mpool_new(t->bt_mp, npg, MPOOL_PAGE_NEXT)); +} diff --git a/src/util/db2/btree/bt_put.c b/src/util/db2/btree/bt_put.c new file mode 100644 index 000000000..abdb86429 --- /dev/null +++ b/src/util/db2/btree/bt_put.c @@ -0,0 +1,320 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_put.c 8.8 (Berkeley) 7/26/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *)); + +/* + * __BT_PUT -- Add a btree item to the tree. + * + * Parameters: + * dbp: pointer to access method + * key: key + * data: data + * flag: R_NOOVERWRITE + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the + * tree and R_NOOVERWRITE specified. + */ +int +__bt_put(dbp, key, data, flags) + const DB *dbp; + DBT *key; + const DBT *data; + u_int flags; +{ + BTREE *t; + DBT tkey, tdata; + EPG *e; + PAGE *h; + indx_t index, nxtindex; + db_pgno_t pg; + u_int32_t nbytes; + int dflags, exact, status; + char *dest, db[NOVFLSIZE], kb[NOVFLSIZE]; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Check for change to a read-only tree. */ + if (F_ISSET(t, B_RDONLY)) { + errno = EPERM; + return (RET_ERROR); + } + + switch (flags) { + case 0: + case R_NOOVERWRITE: + break; + case R_CURSOR: + /* + * If flags is R_CURSOR, put the cursor. Must already + * have started a scan and not have already deleted it. + */ + if (F_ISSET(&t->bt_cursor, CURS_INIT) && + !F_ISSET(&t->bt_cursor, + CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE)) + break; + /* FALLTHROUGH */ + default: + errno = EINVAL; + return (RET_ERROR); + } + + /* + * If the key/data pair won't fit on a page, store it on overflow + * pages. Only put the key on the overflow page if the pair are + * still too big after moving the data to an overflow page. + * + * XXX + * If the insert fails later on, the overflow pages aren't recovered. + */ + dflags = 0; + if (key->size + data->size > t->bt_ovflsize) { + if (key->size > t->bt_ovflsize) { +storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR) + return (RET_ERROR); + tkey.data = kb; + tkey.size = NOVFLSIZE; + memmove(kb, &pg, sizeof(db_pgno_t)); + memmove(kb + sizeof(db_pgno_t), + &key->size, sizeof(u_int32_t)); + dflags |= P_BIGKEY; + key = &tkey; + } + if (key->size + data->size > t->bt_ovflsize) { + if (__ovfl_put(t, data, &pg) == RET_ERROR) + return (RET_ERROR); + tdata.data = db; + tdata.size = NOVFLSIZE; + memmove(db, &pg, sizeof(db_pgno_t)); + memmove(db + sizeof(db_pgno_t), + &data->size, sizeof(u_int32_t)); + dflags |= P_BIGDATA; + data = &tdata; + } + if (key->size + data->size > t->bt_ovflsize) + goto storekey; + } + + /* Replace the cursor. */ + if (flags == R_CURSOR) { + if ((h = mpool_get(t->bt_mp, t->bt_cursor.pg.pgno, 0)) == NULL) + return (RET_ERROR); + index = t->bt_cursor.pg.index; + goto delete; + } + + /* + * Find the key to delete, or, the location at which to insert. + * Bt_fast and __bt_search both pin the returned page. + */ + if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL) + if ((e = __bt_search(t, key, &exact)) == NULL) + return (RET_ERROR); + h = e->page; + index = e->index; + + /* + * Add the key/data pair to the tree. If an identical key is already + * in the tree, and R_NOOVERWRITE is set, an error is returned. If + * R_NOOVERWRITE is not set, the key is either added (if duplicates are + * permitted) or an error is returned. + */ + switch (flags) { + case R_NOOVERWRITE: + if (!exact) + break; + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + default: + if (!exact || !F_ISSET(t, B_NODUPS)) + break; + /* + * !!! + * Note, the delete may empty the page, so we need to put a + * new entry into the page immediately. + */ +delete: if (__bt_dleaf(t, key, h, index) == RET_ERROR) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + break; + } + + /* + * If not enough room, or the user has put a ceiling on the number of + * keys permitted in the page, split the page. The split code will + * insert the key and data and unpin the current page. If inserting + * into the offset array, shift the pointers up. + */ + nbytes = NBLEAFDBT(key->size, data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + if ((status = __bt_split(t, h, key, + data, dflags, nbytes, index)) != RET_SUCCESS) + return (status); + goto success; + } + + if (index < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + index + 1, h->linp + index, + (nxtindex - index) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + + h->linp[index] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_BLEAF(dest, key, data, dflags); + + /* If the cursor is on this page, adjust it as necessary. */ + if (F_ISSET(&t->bt_cursor, CURS_INIT) && + !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) && + t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index >= index) + ++t->bt_cursor.pg.index; + + if (t->bt_order == NOT) + if (h->nextpg == P_INVALID) { + if (index == NEXTINDEX(h) - 1) { + t->bt_order = FORWARD; + t->bt_last.index = index; + t->bt_last.pgno = h->pgno; + } + } else if (h->prevpg == P_INVALID) { + if (index == 0) { + t->bt_order = BACK; + t->bt_last.index = 0; + t->bt_last.pgno = h->pgno; + } + } + + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + +success: + if (flags == R_SETCURSOR) + __bt_setcur(t, e->page->pgno, e->index); + + F_SET(t, B_MODIFIED); + return (RET_SUCCESS); +} + +#ifdef STATISTICS +u_long bt_cache_hit, bt_cache_miss; +#endif + +/* + * BT_FAST -- Do a quick check for sorted data. + * + * Parameters: + * t: tree + * key: key to insert + * + * Returns: + * EPG for new record or NULL if not found. + */ +static EPG * +bt_fast(t, key, data, exactp) + BTREE *t; + const DBT *key, *data; + int *exactp; +{ + PAGE *h; + u_int32_t nbytes; + int cmp; + + if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) { + t->bt_order = NOT; + return (NULL); + } + t->bt_cur.page = h; + t->bt_cur.index = t->bt_last.index; + + /* + * If won't fit in this page or have too many keys in this page, + * have to search to get split stack. + */ + nbytes = NBLEAFDBT(key->size, data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) + goto miss; + + if (t->bt_order == FORWARD) { + if (t->bt_cur.page->nextpg != P_INVALID) + goto miss; + if (t->bt_cur.index != NEXTINDEX(h) - 1) + goto miss; + if ((cmp = __bt_cmp(t, key, &t->bt_cur)) < 0) + goto miss; + t->bt_last.index = cmp ? ++t->bt_cur.index : t->bt_cur.index; + } else { + if (t->bt_cur.page->prevpg != P_INVALID) + goto miss; + if (t->bt_cur.index != 0) + goto miss; + if ((cmp = __bt_cmp(t, key, &t->bt_cur)) > 0) + goto miss; + t->bt_last.index = 0; + } + *exactp = cmp == 0; +#ifdef STATISTICS + ++bt_cache_hit; +#endif + return (&t->bt_cur); + +miss: +#ifdef STATISTICS + ++bt_cache_miss; +#endif + t->bt_order = NOT; + mpool_put(t->bt_mp, h, 0); + return (NULL); +} diff --git a/src/util/db2/btree/bt_search.c b/src/util/db2/btree/bt_search.c new file mode 100644 index 000000000..869f1ef3f --- /dev/null +++ b/src/util/db2/btree/bt_search.c @@ -0,0 +1,297 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_search.c 8.9 (Berkeley) 10/26/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include + +#include "db-int.h" +#include "btree.h" + +static int __bt_snext __P((BTREE *, PAGE *, const DBT *, int *)); +static int __bt_sprev __P((BTREE *, PAGE *, const DBT *, int *)); + +/* + * __bt_search -- + * Search a btree for a key. + * + * Parameters: + * t: tree to search + * key: key to find + * exactp: pointer to exact match flag + * + * Returns: + * The EPG for matching record, if any, or the EPG for the location + * of the key, if it were inserted into the tree, is entered into + * the bt_cur field of the tree. A pointer to the field is returned. + */ +EPG * +__bt_search(t, key, exactp) + BTREE *t; + const DBT *key; + int *exactp; +{ + PAGE *h; + indx_t base, index, lim; + db_pgno_t pg; + int cmp; + + BT_CLR(t); + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (NULL); + + /* Do a binary search on the current page. */ + t->bt_cur.page = h; + for (base = 0, lim = NEXTINDEX(h); lim; lim >>= 1) { + t->bt_cur.index = index = base + (lim >> 1); + if ((cmp = __bt_cmp(t, key, &t->bt_cur)) == 0) { + if (h->flags & P_BLEAF) { + *exactp = 1; + return (&t->bt_cur); + } + goto next; + } + if (cmp > 0) { + base = index + 1; + --lim; + } + } + + /* + * If it's a leaf page, we're almost done. If no duplicates + * are allowed, or we have an exact match, we're done. Else, + * it's possible that there were matching keys on this page, + * which later deleted, and we're on a page with no matches + * while there are matches on other pages. If at the start or + * end of a page, check the adjacent page. + */ + if (h->flags & P_BLEAF) { + if (!F_ISSET(t, B_NODUPS)) { + if (base == 0 && + h->prevpg != P_INVALID && + __bt_sprev(t, h, key, exactp)) + return (&t->bt_cur); + if (base == NEXTINDEX(h) && + h->nextpg != P_INVALID && + __bt_snext(t, h, key, exactp)) + return (&t->bt_cur); + } + *exactp = 0; + t->bt_cur.index = base; + return (&t->bt_cur); + } + + /* + * No match found. Base is the smallest index greater than + * key and may be zero or a last + 1 index. If it's non-zero, + * decrement by one, and record the internal page which should + * be a parent page for the key. If a split later occurs, the + * inserted page will be to the right of the saved page. + */ + index = base ? base - 1 : base; + +next: BT_PUSH(t, h->pgno, index); + pg = GETBINTERNAL(h, index)->pgno; + mpool_put(t->bt_mp, h, 0); + } +} + +/* + * __bt_snext -- + * Check for an exact match after the key. + * + * Parameters: + * t: tree + * h: current page + * key: key + * exactp: pointer to exact match flag + * + * Returns: + * If an exact match found. + */ +static int +__bt_snext(t, h, key, exactp) + BTREE *t; + PAGE *h; + const DBT *key; + int *exactp; +{ + BINTERNAL *bi; + EPG e; + EPGNO *parent; + indx_t index; + db_pgno_t pgno; + int level; + + /* + * Get the next page. The key is either an exact + * match, or not as good as the one we already have. + */ + if ((e.page = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) + return (0); + e.index = 0; + if (__bt_cmp(t, key, &e) != 0) { + mpool_put(t->bt_mp, e.page, 0); + return (0); + } + mpool_put(t->bt_mp, h, 0); + t->bt_cur = e; + *exactp = 1; + + /* + * Adjust the stack for the movement. + * + * Move up the stack. + */ + for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + return (0); + + /* Move to the next index. */ + if (parent->index != NEXTINDEX(h) - 1) { + index = parent->index + 1; + BT_PUSH(t, h->pgno, index); + break; + } + mpool_put(t->bt_mp, h, 0); + } + + /* Restore the stack. */ + while (level--) { + /* Push the next level down onto the stack. */ + bi = GETBINTERNAL(h, index); + pgno = bi->pgno; + BT_PUSH(t, pgno, 0); + + /* Lose the currently pinned page. */ + mpool_put(t->bt_mp, h, 0); + + /* Get the next level down. */ + if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) + return (0); + index = 0; + } + mpool_put(t->bt_mp, h, 0); + return (1); +} + +/* + * __bt_sprev -- + * Check for an exact match before the key. + * + * Parameters: + * t: tree + * h: current page + * key: key + * exactp: pointer to exact match flag + * + * Returns: + * If an exact match found. + */ +static int +__bt_sprev(t, h, key, exactp) + BTREE *t; + PAGE *h; + const DBT *key; + int *exactp; +{ + BINTERNAL *bi; + EPG e; + EPGNO *parent; + indx_t index; + db_pgno_t pgno; + int level; + + /* + * Get the previous page. The key is either an exact + * match, or not as good as the one we already have. + */ + if ((e.page = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL) + return (0); + e.index = NEXTINDEX(e.page) - 1; + if (__bt_cmp(t, key, &e) != 0) { + mpool_put(t->bt_mp, e.page, 0); + return (0); + } + + mpool_put(t->bt_mp, h, 0); + t->bt_cur = e; + *exactp = 1; + + /* + * Adjust the stack for the movement. + * + * Move up the stack. + */ + for (level = 0; (parent = BT_POP(t)) != NULL; ++level) { + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + return (1); + + /* Move to the next index. */ + if (parent->index != 0) { + index = parent->index - 1; + BT_PUSH(t, h->pgno, index); + break; + } + mpool_put(t->bt_mp, h, 0); + } + + /* Restore the stack. */ + while (level--) { + /* Push the next level down onto the stack. */ + bi = GETBINTERNAL(h, index); + pgno = bi->pgno; + + /* Lose the currently pinned page. */ + mpool_put(t->bt_mp, h, 0); + + /* Get the next level down. */ + if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL) + return (1); + + index = NEXTINDEX(h) - 1; + BT_PUSH(t, pgno, index); + } + mpool_put(t->bt_mp, h, 0); + return (1); +} diff --git a/src/util/db2/btree/bt_seq.c b/src/util/db2/btree/bt_seq.c new file mode 100644 index 000000000..3e68c66a9 --- /dev/null +++ b/src/util/db2/btree/bt_seq.c @@ -0,0 +1,490 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_seq.c 8.9 (Berkeley) 6/20/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +static int __bt_first __P((BTREE *, const DBT *, EPG *, int *)); +static int __bt_seqadv __P((BTREE *, EPG *, int)); +static int __bt_seqset __P((BTREE *, EPG *, DBT *, int)); + +/* + * Sequential scan support. + * + * The tree can be scanned sequentially, starting from either end of the + * tree or from any specific key. A scan request before any scanning is + * done is initialized as starting from the least node. + */ + +/* + * __bt_seq -- + * Btree sequential scan interface. + * + * Parameters: + * dbp: pointer to access method + * key: key for positioning and return value + * data: data return value + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +int +__bt_seq(dbp, key, data, flags) + const DB *dbp; + DBT *key, *data; + u_int flags; +{ + BTREE *t; + EPG e; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* + * If scan unitialized as yet, or starting at a specific record, set + * the scan to a specific key. Both __bt_seqset and __bt_seqadv pin + * the page the cursor references if they're successful. + */ + switch (flags) { + case R_NEXT: + case R_PREV: + if (F_ISSET(&t->bt_cursor, CURS_INIT)) { + status = __bt_seqadv(t, &e, flags); + break; + } + /* FALLTHROUGH */ + case R_FIRST: + case R_LAST: + case R_CURSOR: + status = __bt_seqset(t, &e, key, flags); + break; + default: + errno = EINVAL; + return (RET_ERROR); + } + + if (status == RET_SUCCESS) { + __bt_setcur(t, e.page->pgno, e.index); + + status = + __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0); + + /* + * If the user is doing concurrent access, we copied the + * key/data, toss the page. + */ + if (F_ISSET(t, B_DB_LOCK)) + mpool_put(t->bt_mp, e.page, 0); + else + t->bt_pinned = e.page; + } + return (status); +} + +/* + * __bt_seqset -- + * Set the sequential scan to a specific key. + * + * Parameters: + * t: tree + * ep: storage for returned key + * key: key for initial scan position + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV + * + * Side effects: + * Pins the page the cursor references. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +static int +__bt_seqset(t, ep, key, flags) + BTREE *t; + EPG *ep; + DBT *key; + int flags; +{ + PAGE *h; + db_pgno_t pg; + int exact; + + /* + * Find the first, last or specific key in the tree and point the + * cursor at it. The cursor may not be moved until a new key has + * been found. + */ + switch (flags) { + case R_CURSOR: /* Keyed scan. */ + /* + * Find the first instance of the key or the smallest key + * which is greater than or equal to the specified key. + */ + if (key->data == NULL || key->size == 0) { + errno = EINVAL; + return (RET_ERROR); + } + return (__bt_first(t, key, ep, &exact)); + case R_FIRST: /* First record. */ + case R_NEXT: + /* Walk down the left-hand side of the tree. */ + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + /* Check for an empty tree. */ + if (NEXTINDEX(h) == 0) { + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + } + + if (h->flags & (P_BLEAF | P_RLEAF)) + break; + pg = GETBINTERNAL(h, 0)->pgno; + mpool_put(t->bt_mp, h, 0); + } + ep->page = h; + ep->index = 0; + break; + case R_LAST: /* Last record. */ + case R_PREV: + /* Walk down the right-hand side of the tree. */ + for (pg = P_ROOT;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + + /* Check for an empty tree. */ + if (NEXTINDEX(h) == 0) { + mpool_put(t->bt_mp, h, 0); + return (RET_SPECIAL); + } + + if (h->flags & (P_BLEAF | P_RLEAF)) + break; + pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno; + mpool_put(t->bt_mp, h, 0); + } + + ep->page = h; + ep->index = NEXTINDEX(h) - 1; + break; + } + return (RET_SUCCESS); +} + +/* + * __bt_seqadvance -- + * Advance the sequential scan. + * + * Parameters: + * t: tree + * flags: R_NEXT, R_PREV + * + * Side effects: + * Pins the page the new key/data record is on. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +static int +__bt_seqadv(t, ep, flags) + BTREE *t; + EPG *ep; + int flags; +{ + CURSOR *c; + PAGE *h; + indx_t index; + db_pgno_t pg; + int exact, rval; + + /* + * There are a couple of states that we can be in. The cursor has + * been initialized by the time we get here, but that's all we know. + */ + c = &t->bt_cursor; + + /* + * The cursor was deleted and there weren't any duplicate records, + * so the cursor's key was saved. Find out where that key would + * be in the current tree. If the returned key is an exact match, + * it means that a key/data pair was inserted into the tree after + * the delete. We could reasonably return the key, but the problem + * is that this is the access pattern we'll see if the user is + * doing seq(..., R_NEXT)/put(..., 0) pairs, i.e. the put deletes + * the cursor record and then replaces it, so the cursor was saved, + * and we'll simply return the same "new" record until the user + * notices and doesn't do a put() of it. Since the key is an exact + * match, we could as easily put the new record before the cursor, + * and we've made no guarantee to return it. So, move forward or + * back a record if it's an exact match. + * + * XXX + * In the current implementation, put's to the cursor are done with + * delete/add pairs. This has two consequences. First, it means + * that seq(..., R_NEXT)/put(..., R_CURSOR) pairs are going to exhibit + * the same behavior as above. Second, you can return the same key + * twice if you have duplicate records. The scenario is that the + * cursor record is deleted, moving the cursor forward or backward + * to a duplicate. The add then inserts the new record at a location + * ahead of the cursor because duplicates aren't sorted in any way, + * and the new record is later returned. This has to be fixed at some + * point. + */ + if (F_ISSET(c, CURS_ACQUIRE)) { + if ((rval = __bt_first(t, &c->key, ep, &exact)) == RET_ERROR) + return (RET_ERROR); + if (!exact) + return (rval); + /* + * XXX + * Kluge -- get, release, get the page. + */ + c->pg.pgno = ep->page->pgno; + c->pg.index = ep->index; + mpool_put(t->bt_mp, ep->page, 0); + } + + /* Get the page referenced by the cursor. */ + if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) + return (RET_ERROR); + + /* + * Find the next/previous record in the tree and point the cursor at + * it. The cursor may not be moved until a new key has been found. + */ + switch (flags) { + case R_NEXT: /* Next record. */ + /* + * The cursor was deleted in duplicate records, and moved + * forward to a record that has yet to be returned. Clear + * that flag, and return the record. + */ + if (F_ISSET(c, CURS_AFTER)) + goto usecurrent; + index = c->pg.index; + if (++index == NEXTINDEX(h)) { + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + index = 0; + } + break; + case R_PREV: /* Previous record. */ + /* + * The cursor was deleted in duplicate records, and moved + * backward to a record that has yet to be returned. Clear + * that flag, and return the record. + */ + if (F_ISSET(c, CURS_BEFORE)) { +usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE); + ep->page = h; + ep->index = c->pg.index; + return (RET_SUCCESS); + } + index = c->pg.index; + if (index == 0) { + pg = h->prevpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + index = NEXTINDEX(h) - 1; + } else + --index; + break; + } + + ep->page = h; + ep->index = index; + return (RET_SUCCESS); +} + +/* + * __bt_first -- + * Find the first entry. + * + * Parameters: + * t: the tree + * key: the key + * erval: return EPG + * exactp: pointer to exact match flag + * + * Returns: + * The first entry in the tree greater than or equal to key, + * or RET_SPECIAL if no such key exists. + */ +static int +__bt_first(t, key, erval, exactp) + BTREE *t; + const DBT *key; + EPG *erval; + int *exactp; +{ + PAGE *h; + EPG *ep, save; + db_pgno_t pg; + + /* + * Find any matching record; __bt_search pins the page. + * + * If it's an exact match and duplicates are possible, walk backwards + * in the tree until we find the first one. Otherwise, make sure it's + * a valid key (__bt_search may return an index just past the end of a + * page) and return it. + */ + if ((ep = __bt_search(t, key, exactp)) == NULL) + return (RET_SPECIAL); + if (*exactp) { + if (F_ISSET(t, B_NODUPS)) { + *erval = *ep; + return (RET_SUCCESS); + } + + /* + * Walk backwards, as long as the entry matches and there are + * keys left in the tree. Save a copy of each match in case + * we go too far. + */ + save = *ep; + h = ep->page; + do { + if (save.page->pgno != ep->page->pgno) { + mpool_put(t->bt_mp, save.page, 0); + save = *ep; + } else + save.index = ep->index; + + /* + * Don't unpin the page the last (or original) match + * was on, but make sure it's unpinned if an error + * occurs. + */ + if (ep->index == 0) { + if (h->prevpg == P_INVALID) + break; + if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, 0); + if ((h = mpool_get(t->bt_mp, + h->prevpg, 0)) == NULL) { + if (h->pgno == save.page->pgno) + mpool_put(t->bt_mp, + save.page, 0); + return (RET_ERROR); + } + ep->page = h; + ep->index = NEXTINDEX(h); + } + --ep->index; + } while (__bt_cmp(t, key, ep) == 0); + + /* + * Reach here with the last page that was looked at pinned, + * which may or may not be the same as the last (or original) + * match page. If it's not useful, release it. + */ + if (h->pgno != save.page->pgno) + mpool_put(t->bt_mp, h, 0); + + *erval = save; + return (RET_SUCCESS); + } + + /* If at the end of a page, find the next entry. */ + if (ep->index == NEXTINDEX(ep->page)) { + h = ep->page; + pg = h->nextpg; + mpool_put(t->bt_mp, h, 0); + if (pg == P_INVALID) + return (RET_SPECIAL); + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + ep->index = 0; + ep->page = h; + } + *erval = *ep; + return (RET_SUCCESS); +} + +/* + * __bt_setcur -- + * Set the cursor to an entry in the tree. + * + * Parameters: + * t: the tree + * pgno: page number + * index: page index + */ +void +__bt_setcur(t, pgno, index) + BTREE *t; + db_pgno_t pgno; + u_int index; +{ + /* Lose any already deleted key. */ + if (t->bt_cursor.key.data != NULL) { + free(t->bt_cursor.key.data); + t->bt_cursor.key.size = 0; + t->bt_cursor.key.data = NULL; + } + F_CLR(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE); + + /* Update the cursor. */ + t->bt_cursor.pg.pgno = pgno; + t->bt_cursor.pg.index = index; + F_SET(&t->bt_cursor, CURS_INIT); +} diff --git a/src/util/db2/btree/bt_split.c b/src/util/db2/btree/bt_split.c new file mode 100644 index 000000000..0fc95baf3 --- /dev/null +++ b/src/util/db2/btree/bt_split.c @@ -0,0 +1,827 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_split.c 8.10 (Berkeley) 1/9/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +static int bt_broot __P((BTREE *, PAGE *, PAGE *, PAGE *)); +static PAGE *bt_page + __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t)); +static int bt_preserve __P((BTREE *, db_pgno_t)); +static PAGE *bt_psplit + __P((BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t)); +static PAGE *bt_root + __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t)); +static int bt_rroot __P((BTREE *, PAGE *, PAGE *, PAGE *)); +static recno_t rec_total __P((PAGE *)); + +#ifdef STATISTICS +u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved; +#endif + +/* + * __BT_SPLIT -- Split the tree. + * + * Parameters: + * t: tree + * sp: page to split + * key: key to insert + * data: data to insert + * flags: BIGKEY/BIGDATA flags + * ilen: insert length + * skip: index to leave open + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__bt_split(t, sp, key, data, flags, ilen, argskip) + BTREE *t; + PAGE *sp; + const DBT *key, *data; + int flags; + size_t ilen; + u_int32_t argskip; +{ + BINTERNAL *bi; + BLEAF *bl, *tbl; + DBT a, b; + EPGNO *parent; + PAGE *h, *l, *r, *lchild, *rchild; + indx_t nxtindex; + u_int16_t skip; + u_int32_t n, nbytes, nksize; + int parentsplit; + char *dest; + + /* + * Split the page into two pages, l and r. The split routines return + * a pointer to the page into which the key should be inserted and with + * skip set to the offset which should be used. Additionally, l and r + * are pinned. + */ + skip = argskip; + h = sp->pgno == P_ROOT ? + bt_root(t, sp, &l, &r, &skip, ilen) : + bt_page(t, sp, &l, &r, &skip, ilen); + if (h == NULL) + return (RET_ERROR); + + /* + * Insert the new key/data pair into the leaf page. (Key inserts + * always cause a leaf page to split first.) + */ + h->linp[skip] = h->upper -= ilen; + dest = (char *)h + h->upper; + if (F_ISSET(t, R_RECNO)) + WR_RLEAF(dest, data, flags) + else + WR_BLEAF(dest, key, data, flags) + + /* If the root page was split, make it look right. */ + if (sp->pgno == P_ROOT && + (F_ISSET(t, R_RECNO) ? + bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) + goto err2; + + /* + * Now we walk the parent page stack -- a LIFO stack of the pages that + * were traversed when we searched for the page that split. Each stack + * entry is a page number and a page index offset. The offset is for + * the page traversed on the search. We've just split a page, so we + * have to insert a new key into the parent page. + * + * If the insert into the parent page causes it to split, may have to + * continue splitting all the way up the tree. We stop if the root + * splits or the page inserted into didn't have to split to hold the + * new key. Some algorithms replace the key for the old page as well + * as the new page. We don't, as there's no reason to believe that the + * first key on the old page is any better than the key we have, and, + * in the case of a key being placed at index 0 causing the split, the + * key is unavailable. + * + * There are a maximum of 5 pages pinned at any time. We keep the left + * and right pages pinned while working on the parent. The 5 are the + * two children, left parent and right parent (when the parent splits) + * and the root page or the overflow key page when calling bt_preserve. + * This code must make sure that all pins are released other than the + * root page or overflow page which is unlocked elsewhere. + */ + while ((parent = BT_POP(t)) != NULL) { + lchild = l; + rchild = r; + + /* Get the parent page. */ + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + goto err2; + + /* + * The new key goes ONE AFTER the index, because the split + * was to the right. + */ + skip = parent->index + 1; + + /* + * Calculate the space needed on the parent page. + * + * Prefix trees: space hack when inserting into BINTERNAL + * pages. Retain only what's needed to distinguish between + * the new entry and the LAST entry on the page to its left. + * If the keys compare equal, retain the entire key. Note, + * we don't touch overflow keys, and the entire key must be + * retained for the next-to-left most key on the leftmost + * page of each level, or the search will fail. Applicable + * ONLY to internal pages that have leaf pages as children. + * Further reduction of the key between pairs of internal + * pages loses too much information. + */ + switch (rchild->flags & P_TYPE) { + case P_BINTERNAL: + bi = GETBINTERNAL(rchild, 0); + nbytes = NBINTERNAL(bi->ksize); + break; + case P_BLEAF: + bl = GETBLEAF(rchild, 0); + nbytes = NBINTERNAL(bl->ksize); + if (t->bt_pfx && !(bl->flags & P_BIGKEY) && + (h->prevpg != P_INVALID || skip > 1)) { + tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1); + a.size = tbl->ksize; + a.data = tbl->bytes; + b.size = bl->ksize; + b.data = bl->bytes; + nksize = t->bt_pfx(&a, &b); + n = NBINTERNAL(nksize); + if (n < nbytes) { +#ifdef STATISTICS + bt_pfxsaved += nbytes - n; +#endif + nbytes = n; + } else + nksize = 0; + } else + nksize = 0; + break; + case P_RINTERNAL: + case P_RLEAF: + nbytes = NRINTERNAL; + break; + default: + abort(); + } + + /* Split the parent page if necessary or shift the indices. */ + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + sp = h; + h = h->pgno == P_ROOT ? + bt_root(t, h, &l, &r, &skip, nbytes) : + bt_page(t, h, &l, &r, &skip, nbytes); + if (h == NULL) + goto err1; + parentsplit = 1; + } else { + if (skip < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + skip + 1, h->linp + skip, + (nxtindex - skip) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + parentsplit = 0; + } + + /* Insert the key into the parent page. */ + switch (rchild->flags & P_TYPE) { + case P_BINTERNAL: + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + memmove(dest, bi, nbytes); + ((BINTERNAL *)dest)->pgno = rchild->pgno; + break; + case P_BLEAF: + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + WR_BINTERNAL(dest, nksize ? nksize : bl->ksize, + rchild->pgno, bl->flags & P_BIGKEY); + memmove(dest, bl->bytes, nksize ? nksize : bl->ksize); + if (bl->flags & P_BIGKEY && + bt_preserve(t, *(db_pgno_t *)bl->bytes) == RET_ERROR) + goto err1; + break; + case P_RINTERNAL: + /* + * Update the left page count. If split + * added at index 0, fix the correct page. + */ + if (skip > 0) + dest = (char *)h + h->linp[skip - 1]; + else + dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; + ((RINTERNAL *)dest)->nrecs = rec_total(lchild); + ((RINTERNAL *)dest)->pgno = lchild->pgno; + + /* Update the right page count. */ + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + ((RINTERNAL *)dest)->nrecs = rec_total(rchild); + ((RINTERNAL *)dest)->pgno = rchild->pgno; + break; + case P_RLEAF: + /* + * Update the left page count. If split + * added at index 0, fix the correct page. + */ + if (skip > 0) + dest = (char *)h + h->linp[skip - 1]; + else + dest = (char *)l + l->linp[NEXTINDEX(l) - 1]; + ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild); + ((RINTERNAL *)dest)->pgno = lchild->pgno; + + /* Update the right page count. */ + h->linp[skip] = h->upper -= nbytes; + dest = (char *)h + h->linp[skip]; + ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild); + ((RINTERNAL *)dest)->pgno = rchild->pgno; + break; + default: + abort(); + } + + /* Unpin the held pages. */ + if (!parentsplit) { + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + } + + /* If the root page was split, make it look right. */ + if (sp->pgno == P_ROOT && + (F_ISSET(t, R_RECNO) ? + bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR) + goto err1; + + mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); + mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); + } + + /* Unpin the held pages. */ + mpool_put(t->bt_mp, l, MPOOL_DIRTY); + mpool_put(t->bt_mp, r, MPOOL_DIRTY); + + /* Clear any pages left on the stack. */ + return (RET_SUCCESS); + + /* + * If something fails in the above loop we were already walking back + * up the tree and the tree is now inconsistent. Nothing much we can + * do about it but release any memory we're holding. + */ +err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY); + mpool_put(t->bt_mp, rchild, MPOOL_DIRTY); + +err2: mpool_put(t->bt_mp, l, 0); + mpool_put(t->bt_mp, r, 0); + __dbpanic(t->bt_dbp); + return (RET_ERROR); +} + +/* + * BT_PAGE -- Split a non-root page of a btree. + * + * Parameters: + * t: tree + * h: root page + * lp: pointer to left page pointer + * rp: pointer to right page pointer + * skip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert or NULL on error. + */ +static PAGE * +bt_page(t, h, lp, rp, skip, ilen) + BTREE *t; + PAGE *h, **lp, **rp; + indx_t *skip; + size_t ilen; +{ + PAGE *l, *r, *tp; + db_pgno_t npg; + +#ifdef STATISTICS + ++bt_split; +#endif + /* Put the new right page for the split into place. */ + if ((r = __bt_new(t, &npg)) == NULL) + return (NULL); + r->pgno = npg; + r->lower = BTDATAOFF; + r->upper = t->bt_psize; + r->nextpg = h->nextpg; + r->prevpg = h->pgno; + r->flags = h->flags & P_TYPE; + + /* + * If we're splitting the last page on a level because we're appending + * a key to it (skip is NEXTINDEX()), it's likely that the data is + * sorted. Adding an empty page on the side of the level is less work + * and can push the fill factor much higher than normal. If we're + * wrong it's no big deal, we'll just do the split the right way next + * time. It may look like it's equally easy to do a similar hack for + * reverse sorted data, that is, split the tree left, but it's not. + * Don't even try. + */ + if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) { +#ifdef STATISTICS + ++bt_sortsplit; +#endif + h->nextpg = r->pgno; + r->lower = BTDATAOFF + sizeof(indx_t); + *skip = 0; + *lp = h; + *rp = r; + return (r); + } + + /* Put the new left page for the split into place. */ + if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) { + mpool_put(t->bt_mp, r, 0); + return (NULL); + } +#ifdef PURIFY + memset(l, 0xff, t->bt_psize); +#endif + l->pgno = h->pgno; + l->nextpg = r->pgno; + l->prevpg = h->prevpg; + l->lower = BTDATAOFF; + l->upper = t->bt_psize; + l->flags = h->flags & P_TYPE; + + /* Fix up the previous pointer of the page after the split page. */ + if (h->nextpg != P_INVALID) { + if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) { + free(l); + /* XXX mpool_free(t->bt_mp, r->pgno); */ + return (NULL); + } + tp->prevpg = r->pgno; + mpool_put(t->bt_mp, tp, MPOOL_DIRTY); + } + + /* + * Split right. The key/data pairs aren't sorted in the btree page so + * it's simpler to copy the data from the split page onto two new pages + * instead of copying half the data to the right page and compacting + * the left page in place. Since the left page can't change, we have + * to swap the original and the allocated left page after the split. + */ + tp = bt_psplit(t, h, l, r, skip, ilen); + + /* Move the new left page onto the old left page. */ + memmove(h, l, t->bt_psize); + if (tp == l) + tp = h; + free(l); + + *lp = h; + *rp = r; + return (tp); +} + +/* + * BT_ROOT -- Split the root page of a btree. + * + * Parameters: + * t: tree + * h: root page + * lp: pointer to left page pointer + * rp: pointer to right page pointer + * skip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert or NULL on error. + */ +static PAGE * +bt_root(t, h, lp, rp, skip, ilen) + BTREE *t; + PAGE *h, **lp, **rp; + indx_t *skip; + size_t ilen; +{ + PAGE *l, *r, *tp; + db_pgno_t lnpg, rnpg; + +#ifdef STATISTICS + ++bt_split; + ++bt_rootsplit; +#endif + /* Put the new left and right pages for the split into place. */ + if ((l = __bt_new(t, &lnpg)) == NULL || + (r = __bt_new(t, &rnpg)) == NULL) + return (NULL); + l->pgno = lnpg; + r->pgno = rnpg; + l->nextpg = r->pgno; + r->prevpg = l->pgno; + l->prevpg = r->nextpg = P_INVALID; + l->lower = r->lower = BTDATAOFF; + l->upper = r->upper = t->bt_psize; + l->flags = r->flags = h->flags & P_TYPE; + + /* Split the root page. */ + tp = bt_psplit(t, h, l, r, skip, ilen); + + *lp = l; + *rp = r; + return (tp); +} + +/* + * BT_RROOT -- Fix up the recno root page after it has been split. + * + * Parameters: + * t: tree + * h: root page + * l: left page + * r: right page + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_rroot(t, h, l, r) + BTREE *t; + PAGE *h, *l, *r; +{ + char *dest; + + /* Insert the left and right keys, set the header information. */ + h->linp[0] = h->upper = t->bt_psize - NRINTERNAL; + dest = (char *)h + h->upper; + WR_RINTERNAL(dest, + l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno); + + h->linp[1] = h->upper -= NRINTERNAL; + dest = (char *)h + h->upper; + WR_RINTERNAL(dest, + r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno); + + h->lower = BTDATAOFF + 2 * sizeof(indx_t); + + /* Unpin the root page, set to recno internal page. */ + h->flags &= ~P_TYPE; + h->flags |= P_RINTERNAL; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} + +/* + * BT_BROOT -- Fix up the btree root page after it has been split. + * + * Parameters: + * t: tree + * h: root page + * l: left page + * r: right page + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +static int +bt_broot(t, h, l, r) + BTREE *t; + PAGE *h, *l, *r; +{ + BINTERNAL *bi; + BLEAF *bl; + u_int32_t nbytes; + char *dest; + + /* + * If the root page was a leaf page, change it into an internal page. + * We copy the key we split on (but not the key's data, in the case of + * a leaf page) to the new root page. + * + * The btree comparison code guarantees that the left-most key on any + * level of the tree is never used, so it doesn't need to be filled in. + */ + nbytes = NBINTERNAL(0); + h->linp[0] = h->upper = t->bt_psize - nbytes; + dest = (char *)h + h->upper; + WR_BINTERNAL(dest, 0, l->pgno, 0); + + switch (h->flags & P_TYPE) { + case P_BLEAF: + bl = GETBLEAF(r, 0); + nbytes = NBINTERNAL(bl->ksize); + h->linp[1] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_BINTERNAL(dest, bl->ksize, r->pgno, 0); + memmove(dest, bl->bytes, bl->ksize); + + /* + * If the key is on an overflow page, mark the overflow chain + * so it isn't deleted when the leaf copy of the key is deleted. + */ + if (bl->flags & P_BIGKEY && + bt_preserve(t, *(db_pgno_t *)bl->bytes) == RET_ERROR) + return (RET_ERROR); + break; + case P_BINTERNAL: + bi = GETBINTERNAL(r, 0); + nbytes = NBINTERNAL(bi->ksize); + h->linp[1] = h->upper -= nbytes; + dest = (char *)h + h->upper; + memmove(dest, bi, nbytes); + ((BINTERNAL *)dest)->pgno = r->pgno; + break; + default: + abort(); + } + + /* There are two keys on the page. */ + h->lower = BTDATAOFF + 2 * sizeof(indx_t); + + /* Unpin the root page, set to btree internal page. */ + h->flags &= ~P_TYPE; + h->flags |= P_BINTERNAL; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} + +/* + * BT_PSPLIT -- Do the real work of splitting the page. + * + * Parameters: + * t: tree + * h: page to be split + * l: page to put lower half of data + * r: page to put upper half of data + * pskip: pointer to index to leave open + * ilen: insert length + * + * Returns: + * Pointer to page in which to insert. + */ +static PAGE * +bt_psplit(t, h, l, r, pskip, ilen) + BTREE *t; + PAGE *h, *l, *r; + indx_t *pskip; + size_t ilen; +{ + BINTERNAL *bi; + BLEAF *bl; + CURSOR *c; + RLEAF *rl; + PAGE *rval; + void *src; + indx_t full, half, nxt, off, skip, top, used; + u_int32_t nbytes; + int bigkeycnt, isbigkey; + + /* + * Split the data to the left and right pages. Leave the skip index + * open. Additionally, make some effort not to split on an overflow + * key. This makes internal page processing faster and can save + * space as overflow keys used by internal pages are never deleted. + */ + bigkeycnt = 0; + skip = *pskip; + full = t->bt_psize - BTDATAOFF; + half = full / 2; + used = 0; + for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) { + if (skip == off) { + nbytes = ilen; + isbigkey = 0; /* XXX: not really known. */ + } else + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + src = bi = GETBINTERNAL(h, nxt); + nbytes = NBINTERNAL(bi->ksize); + isbigkey = bi->flags & P_BIGKEY; + break; + case P_BLEAF: + src = bl = GETBLEAF(h, nxt); + nbytes = NBLEAF(bl); + isbigkey = bl->flags & P_BIGKEY; + break; + case P_RINTERNAL: + src = GETRINTERNAL(h, nxt); + nbytes = NRINTERNAL; + isbigkey = 0; + break; + case P_RLEAF: + src = rl = GETRLEAF(h, nxt); + nbytes = NRLEAF(rl); + isbigkey = 0; + break; + default: + abort(); + } + + /* + * If the key/data pairs are substantial fractions of the max + * possible size for the page, it's possible to get situations + * where we decide to try and copy too much onto the left page. + * Make sure that doesn't happen. + */ + if (skip <= off && used + nbytes >= full || nxt == top - 1) { + --off; + break; + } + + /* Copy the key/data pair, if not the skipped index. */ + if (skip != off) { + ++nxt; + + l->linp[off] = l->upper -= nbytes; + memmove((char *)l + l->upper, src, nbytes); + } + + used += nbytes; + if (used >= half) { + if (!isbigkey || bigkeycnt == 3) + break; + else + ++bigkeycnt; + } + } + + /* + * Off is the last offset that's valid for the left page. + * Nxt is the first offset to be placed on the right page. + */ + l->lower += (off + 1) * sizeof(indx_t); + + /* + * If splitting the page that the cursor was on, the cursor has to be + * adjusted to point to the same record as before the split. If the + * cursor is at or past the skipped slot, the cursor is incremented by + * one. If the cursor is on the right page, it is decremented by the + * number of records split to the left page. + */ + c = &t->bt_cursor; + if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) { + if (c->pg.index >= skip) + ++c->pg.index; + if (c->pg.index < nxt) /* Left page. */ + c->pg.pgno = l->pgno; + else { /* Right page. */ + c->pg.pgno = r->pgno; + c->pg.index -= nxt; + } + } + + /* + * If the skipped index was on the left page, just return that page. + * Otherwise, adjust the skip index to reflect the new position on + * the right page. + */ + if (skip <= off) { + skip = 0; + rval = l; + } else { + rval = r; + *pskip -= nxt; + } + + for (off = 0; nxt < top; ++off) { + if (skip == nxt) { + ++off; + skip = 0; + } + switch (h->flags & P_TYPE) { + case P_BINTERNAL: + src = bi = GETBINTERNAL(h, nxt); + nbytes = NBINTERNAL(bi->ksize); + break; + case P_BLEAF: + src = bl = GETBLEAF(h, nxt); + nbytes = NBLEAF(bl); + break; + case P_RINTERNAL: + src = GETRINTERNAL(h, nxt); + nbytes = NRINTERNAL; + break; + case P_RLEAF: + src = rl = GETRLEAF(h, nxt); + nbytes = NRLEAF(rl); + break; + default: + abort(); + } + ++nxt; + r->linp[off] = r->upper -= nbytes; + memmove((char *)r + r->upper, src, nbytes); + } + r->lower += off * sizeof(indx_t); + + /* If the key is being appended to the page, adjust the index. */ + if (skip == top) + r->lower += sizeof(indx_t); + + return (rval); +} + +/* + * BT_PRESERVE -- Mark a chain of pages as used by an internal node. + * + * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the + * record that references them gets deleted. Chains pointed to by internal + * pages never get deleted. This routine marks a chain as pointed to by an + * internal page. + * + * Parameters: + * t: tree + * pg: page number of first page in the chain. + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +static int +bt_preserve(t, pg) + BTREE *t; + db_pgno_t pg; +{ + PAGE *h; + + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + return (RET_ERROR); + h->flags |= P_PRESERVE; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +/* + * REC_TOTAL -- Return the number of recno entries below a page. + * + * Parameters: + * h: page + * + * Returns: + * The number of recno entries below a page. + * + * XXX + * These values could be set by the bt_psplit routine. The problem is that the + * entry has to be popped off of the stack etc. or the values have to be passed + * all the way back to bt_split/bt_rroot and it's not very clean. + */ +static recno_t +rec_total(h) + PAGE *h; +{ + recno_t recs; + indx_t nxt, top; + + for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt) + recs += GETRINTERNAL(h, nxt)->nrecs; + return (recs); +} diff --git a/src/util/db2/btree/bt_utils.c b/src/util/db2/btree/bt_utils.c new file mode 100644 index 000000000..1a34598ad --- /dev/null +++ b/src/util/db2/btree/bt_utils.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bt_utils.c 8.8 (Berkeley) 7/20/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "btree.h" + +/* + * __bt_ret -- + * Build return key/data pair. + * + * Parameters: + * t: tree + * e: key/data pair to be returned + * key: user's key structure (NULL if not to be filled in) + * rkey: memory area to hold key + * data: user's data structure (NULL if not to be filled in) + * rdata: memory area to hold data + * copy: always copy the key/data item + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__bt_ret(t, e, key, rkey, data, rdata, copy) + BTREE *t; + EPG *e; + DBT *key, *rkey, *data, *rdata; + int copy; +{ + BLEAF *bl; + void *p; + + bl = GETBLEAF(e->page, e->index); + + /* + * We must copy big keys/data to make them contigous. Otherwise, + * leave the page pinned and don't copy unless the user specified + * concurrent access. + */ + if (key == NULL) + goto dataonly; + + if (bl->flags & P_BIGKEY) { + if (__ovfl_get(t, bl->bytes, + &key->size, &rkey->data, &rkey->size)) + return (RET_ERROR); + key->data = rkey->data; + } else if (copy || F_ISSET(t, B_DB_LOCK)) { + if (bl->ksize > rkey->size) { + p = (void *)(rkey->data == NULL ? + malloc(bl->ksize) : realloc(rkey->data, bl->ksize)); + if (p == NULL) + return (RET_ERROR); + rkey->data = p; + rkey->size = bl->ksize; + } + memmove(rkey->data, bl->bytes, bl->ksize); + key->size = bl->ksize; + key->data = rkey->data; + } else { + key->size = bl->ksize; + key->data = bl->bytes; + } + +dataonly: + if (data == NULL) + return (RET_SUCCESS); + + if (bl->flags & P_BIGDATA) { + if (__ovfl_get(t, bl->bytes + bl->ksize, + &data->size, &rdata->data, &rdata->size)) + return (RET_ERROR); + data->data = rdata->data; + } else if (copy || F_ISSET(t, B_DB_LOCK)) { + /* Use +1 in case the first record retrieved is 0 length. */ + if (bl->dsize + 1 > rdata->size) { + p = (void *)(rdata->data == NULL ? + malloc(bl->dsize + 1) : + realloc(rdata->data, bl->dsize + 1)); + if (p == NULL) + return (RET_ERROR); + rdata->data = p; + rdata->size = bl->dsize + 1; + } + memmove(rdata->data, bl->bytes + bl->ksize, bl->dsize); + data->size = bl->dsize; + data->data = rdata->data; + } else { + data->size = bl->dsize; + data->data = bl->bytes + bl->ksize; + } + + return (RET_SUCCESS); +} + +/* + * __BT_CMP -- Compare a key to a given record. + * + * Parameters: + * t: tree + * k1: DBT pointer of first arg to comparison + * e: pointer to EPG for comparison + * + * Returns: + * < 0 if k1 is < record + * = 0 if k1 is = record + * > 0 if k1 is > record + */ +int +__bt_cmp(t, k1, e) + BTREE *t; + const DBT *k1; + EPG *e; +{ + BINTERNAL *bi; + BLEAF *bl; + DBT k2; + PAGE *h; + void *bigkey; + + /* + * The left-most key on internal pages, at any level of the tree, is + * guaranteed by the following code to be less than any user key. + * This saves us from having to update the leftmost key on an internal + * page when the user inserts a new key in the tree smaller than + * anything we've yet seen. + */ + h = e->page; + if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & P_BLEAF)) + return (1); + + bigkey = NULL; + if (h->flags & P_BLEAF) { + bl = GETBLEAF(h, e->index); + if (bl->flags & P_BIGKEY) + bigkey = bl->bytes; + else { + k2.data = bl->bytes; + k2.size = bl->ksize; + } + } else { + bi = GETBINTERNAL(h, e->index); + if (bi->flags & P_BIGKEY) + bigkey = bi->bytes; + else { + k2.data = bi->bytes; + k2.size = bi->ksize; + } + } + + if (bigkey) { + if (__ovfl_get(t, bigkey, + &k2.size, &t->bt_rdata.data, &t->bt_rdata.size)) + return (RET_ERROR); + k2.data = t->bt_rdata.data; + } + return ((*t->bt_cmp)(k1, &k2)); +} + +/* + * __BT_DEFCMP -- Default comparison routine. + * + * Parameters: + * a: DBT #1 + * b: DBT #2 + * + * Returns: + * < 0 if a is < b + * = 0 if a is = b + * > 0 if a is > b + */ +int +__bt_defcmp(a, b) + const DBT *a, *b; +{ + register size_t len; + register u_char *p1, *p2; + + /* + * XXX + * If a size_t doesn't fit in an int, this routine can lose. + * What we need is a integral type which is guaranteed to be + * larger than a size_t, and there is no such thing. + */ + len = MIN(a->size, b->size); + for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2) + if (*p1 != *p2) + return ((int)*p1 - (int)*p2); + return ((int)a->size - (int)b->size); +} + +/* + * __BT_DEFPFX -- Default prefix routine. + * + * Parameters: + * a: DBT #1 + * b: DBT #2 + * + * Returns: + * Number of bytes needed to distinguish b from a. + */ +size_t +__bt_defpfx(a, b) + const DBT *a, *b; +{ + register u_char *p1, *p2; + register size_t cnt, len; + + cnt = 1; + len = MIN(a->size, b->size); + for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2, ++cnt) + if (*p1 != *p2) + return (cnt); + + /* a->size must be <= b->size, or they wouldn't be in this order. */ + return (a->size < b->size ? a->size + 1 : a->size); +} diff --git a/src/util/db2/btree/btree.h b/src/util/db2/btree/btree.h new file mode 100644 index 000000000..171712749 --- /dev/null +++ b/src/util/db2/btree/btree.h @@ -0,0 +1,383 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)btree.h 8.11 (Berkeley) 8/17/94 + */ + +/* Macros to set/clear/test flags. */ +#define F_SET(p, f) (p)->flags |= (f) +#define F_CLR(p, f) (p)->flags &= ~(f) +#define F_ISSET(p, f) ((p)->flags & (f)) + +#include "mpool.h" + +#define DEFMINKEYPAGE (2) /* Minimum keys per page */ +#define MINCACHE (5) /* Minimum cached pages */ +#define MINPSIZE (512) /* Minimum page size */ + +/* + * Page 0 of a btree file contains a copy of the meta-data. This page is also + * used as an out-of-band page, i.e. page pointers that point to nowhere point + * to page 0. Page 1 is the root of the btree. + */ +#define P_INVALID 0 /* Invalid tree page number. */ +#define P_META 0 /* Tree metadata page number. */ +#define P_ROOT 1 /* Tree root page number. */ + +/* + * There are five page layouts in the btree: btree internal pages (BINTERNAL), + * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages + * (RLEAF) and overflow pages. All five page types have a page header (PAGE). + * This implementation requires that values within structures NOT be padded. + * (ANSI C permits random padding.) If your compiler pads randomly you'll have + * to do some work to get this package to run. + */ +typedef struct _page { + db_pgno_t pgno; /* this page's page number */ + db_pgno_t prevpg; /* left sibling */ + db_pgno_t nextpg; /* right sibling */ + +#define P_BINTERNAL 0x01 /* btree internal page */ +#define P_BLEAF 0x02 /* leaf page */ +#define P_OVERFLOW 0x04 /* overflow page */ +#define P_RINTERNAL 0x08 /* recno internal page */ +#define P_RLEAF 0x10 /* leaf page */ +#define P_TYPE 0x1f /* type mask */ +#define P_PRESERVE 0x20 /* never delete this chain of pages */ + u_int32_t flags; + + indx_t lower; /* lower bound of free space on page */ + indx_t upper; /* upper bound of free space on page */ + indx_t linp[1]; /* indx_t-aligned VAR. LENGTH DATA */ +} PAGE; + +/* First and next index. */ +#define BTDATAOFF \ + (sizeof(db_pgno_t) + sizeof(db_pgno_t) + sizeof(db_pgno_t) + \ + sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t)) +#define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t)) + +/* + * For pages other than overflow pages, there is an array of offsets into the + * rest of the page immediately following the page header. Each offset is to + * an item which is unique to the type of page. The h_lower offset is just + * past the last filled-in index. The h_upper offset is the first item on the + * page. Offsets are from the beginning of the page. + * + * If an item is too big to store on a single page, a flag is set and the item + * is a { page, size } pair such that the page is the first page of an overflow + * chain with size bytes of item. Overflow pages are simply bytes without any + * external structure. + * + * The page number and size fields in the items are db_pgno_t-aligned so they can + * be manipulated without copying. (This presumes that 32 bit items can be + * manipulated on this system.) + */ +#define LALIGN(n) (((n) + sizeof(db_pgno_t) - 1) & ~(sizeof(db_pgno_t) - 1)) +#define NOVFLSIZE (sizeof(db_pgno_t) + sizeof(u_int32_t)) + +/* + * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno} + * pairs, such that the key compares less than or equal to all of the records + * on that page. For a tree without duplicate keys, an internal page with two + * consecutive keys, a and b, will have all records greater than or equal to a + * and less than b stored on the page associated with a. Duplicate keys are + * somewhat special and can cause duplicate internal and leaf page records and + * some minor modifications of the above rule. + */ +typedef struct _binternal { + u_int32_t ksize; /* key size */ + db_pgno_t pgno; /* page number stored on */ +#define P_BIGDATA 0x01 /* overflow data */ +#define P_BIGKEY 0x02 /* overflow key */ + u_char flags; + char bytes[1]; /* data */ +} BINTERNAL; + +/* Get the page's BINTERNAL structure at index indx. */ +#define GETBINTERNAL(pg, indx) \ + ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NBINTERNAL(len) \ + LALIGN(sizeof(u_int32_t) + sizeof(db_pgno_t) + sizeof(u_char) + (len)) + +/* Copy a BINTERNAL entry to the page. */ +#define WR_BINTERNAL(p, size, pgno, flags) { \ + *(u_int32_t *)p = size; \ + p += sizeof(u_int32_t); \ + *(db_pgno_t *)p = pgno; \ + p += sizeof(db_pgno_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ +} + +/* + * For the recno internal pages, the item is a page number with the number of + * keys found on that page and below. + */ +typedef struct _rinternal { + recno_t nrecs; /* number of records */ + db_pgno_t pgno; /* page number stored below */ +} RINTERNAL; + +/* Get the page's RINTERNAL structure at index indx. */ +#define GETRINTERNAL(pg, indx) \ + ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NRINTERNAL \ + LALIGN(sizeof(recno_t) + sizeof(db_pgno_t)) + +/* Copy a RINTERAL entry to the page. */ +#define WR_RINTERNAL(p, nrecs, pgno) { \ + *(recno_t *)p = nrecs; \ + p += sizeof(recno_t); \ + *(db_pgno_t *)p = pgno; \ +} + +/* For the btree leaf pages, the item is a key and data pair. */ +typedef struct _bleaf { + u_int32_t ksize; /* size of key */ + u_int32_t dsize; /* size of data */ + u_char flags; /* P_BIGDATA, P_BIGKEY */ + char bytes[1]; /* data */ +} BLEAF; + +/* Get the page's BLEAF structure at index indx. */ +#define GETBLEAF(pg, indx) \ + ((BLEAF *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize) + +/* Get the number of bytes in the user's key/data pair. */ +#define NBLEAFDBT(ksize, dsize) \ + LALIGN(sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(u_char) + \ + (ksize) + (dsize)) + +/* Copy a BLEAF entry to the page. */ +#define WR_BLEAF(p, key, data, flags) { \ + *(u_int32_t *)p = key->size; \ + p += sizeof(u_int32_t); \ + *(u_int32_t *)p = data->size; \ + p += sizeof(u_int32_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ + memmove(p, key->data, key->size); \ + p += key->size; \ + memmove(p, data->data, data->size); \ +} + +/* For the recno leaf pages, the item is a data entry. */ +typedef struct _rleaf { + u_int32_t dsize; /* size of data */ + u_char flags; /* P_BIGDATA */ + char bytes[1]; +} RLEAF; + +/* Get the page's RLEAF structure at index indx. */ +#define GETRLEAF(pg, indx) \ + ((RLEAF *)((char *)(pg) + (pg)->linp[indx])) + +/* Get the number of bytes in the entry. */ +#define NRLEAF(p) NRLEAFDBT((p)->dsize) + +/* Get the number of bytes from the user's data. */ +#define NRLEAFDBT(dsize) \ + LALIGN(sizeof(u_int32_t) + sizeof(u_char) + (dsize)) + +/* Copy a RLEAF entry to the page. */ +#define WR_RLEAF(p, data, flags) { \ + *(u_int32_t *)p = data->size; \ + p += sizeof(u_int32_t); \ + *(u_char *)p = flags; \ + p += sizeof(u_char); \ + memmove(p, data->data, data->size); \ +} + +/* + * A record in the tree is either a pointer to a page and an index in the page + * or a page number and an index. These structures are used as a cursor, stack + * entry and search returns as well as to pass records to other routines. + * + * One comment about searches. Internal page searches must find the largest + * record less than key in the tree so that descents work. Leaf page searches + * must find the smallest record greater than key so that the returned index + * is the record's correct position for insertion. + */ +typedef struct _epgno { + db_pgno_t pgno; /* the page number */ + indx_t index; /* the index on the page */ +} EPGNO; + +typedef struct _epg { + PAGE *page; /* the (pinned) page */ + indx_t index; /* the index on the page */ +} EPG; + +/* + * About cursors. The cursor (and the page that contained the key/data pair + * that it referenced) can be deleted, which makes things a bit tricky. If + * there are no duplicates of the cursor key in the tree (i.e. B_NODUPS is set + * or there simply aren't any duplicates of the key) we copy the key that it + * referenced when it's deleted, and reacquire a new cursor key if the cursor + * is used again. If there are duplicates keys, we move to the next/previous + * key, and set a flag so that we know what happened. NOTE: if duplicate (to + * the cursor) keys are added to the tree during this process, it is undefined + * if they will be returned or not in a cursor scan. + * + * The flags determine the possible states of the cursor: + * + * CURS_INIT The cursor references *something*. + * CURS_ACQUIRE The cursor was deleted, and a key has been saved so that + * we can reacquire the right position in the tree. + * CURS_AFTER, CURS_BEFORE + * The cursor was deleted, and now references a key/data pair + * that has not yet been returned, either before or after the + * deleted key/data pair. + * XXX + * This structure is broken out so that we can eventually offer multiple + * cursors as part of the DB interface. + */ +typedef struct _cursor { + EPGNO pg; /* B: Saved tree reference. */ + DBT key; /* B: Saved key, or key.data == NULL. */ + recno_t rcursor; /* R: recno cursor (1-based) */ + +#define CURS_ACQUIRE 0x01 /* B: Cursor needs to be reacquired. */ +#define CURS_AFTER 0x02 /* B: Unreturned cursor after key. */ +#define CURS_BEFORE 0x04 /* B: Unreturned cursor before key. */ +#define CURS_INIT 0x08 /* RB: Cursor initialized. */ + u_int8_t flags; +} CURSOR; + +/* + * The metadata of the tree. The nrecs field is used only by the RECNO code. + * This is because the btree doesn't really need it and it requires that every + * put or delete call modify the metadata. + */ +typedef struct _btmeta { + u_int32_t magic; /* magic number */ + u_int32_t version; /* version */ + u_int32_t psize; /* page size */ + u_int32_t free; /* page number of first free page */ + u_int32_t nrecs; /* R: number of records */ + +#define SAVEMETA (B_NODUPS | R_RECNO) + u_int32_t flags; /* bt_flags & SAVEMETA */ +} BTMETA; + +/* The in-memory btree/recno data structure. */ +typedef struct _btree { + MPOOL *bt_mp; /* memory pool cookie */ + + DB *bt_dbp; /* pointer to enclosing DB */ + + EPG bt_cur; /* current (pinned) page */ + PAGE *bt_pinned; /* page pinned across calls */ + + CURSOR bt_cursor; /* cursor */ + +#define BT_PUSH(t, p, i) { \ + t->bt_sp->pgno = p; \ + t->bt_sp->index = i; \ + ++t->bt_sp; \ +} +#define BT_POP(t) (t->bt_sp == t->bt_stack ? NULL : --t->bt_sp) +#define BT_CLR(t) (t->bt_sp = t->bt_stack) + EPGNO bt_stack[50]; /* stack of parent pages */ + EPGNO *bt_sp; /* current stack pointer */ + + DBT bt_rkey; /* returned key */ + DBT bt_rdata; /* returned data */ + + int bt_fd; /* tree file descriptor */ + + db_pgno_t bt_free; /* next free page */ + u_int32_t bt_psize; /* page size */ + indx_t bt_ovflsize; /* cut-off for key/data overflow */ + int bt_lorder; /* byte order */ + /* sorted order */ + enum { NOT, BACK, FORWARD } bt_order; + EPGNO bt_last; /* last insert */ + + /* B: key comparison function */ + int (*bt_cmp) __P((const DBT *, const DBT *)); + /* B: prefix comparison function */ + size_t (*bt_pfx) __P((const DBT *, const DBT *)); + /* R: recno input function */ + int (*bt_irec) __P((struct _btree *, recno_t)); + + FILE *bt_rfp; /* R: record FILE pointer */ + int bt_rfd; /* R: record file descriptor */ + + caddr_t bt_cmap; /* R: current point in mapped space */ + caddr_t bt_smap; /* R: start of mapped space */ + caddr_t bt_emap; /* R: end of mapped space */ + size_t bt_msize; /* R: size of mapped region. */ + + recno_t bt_nrecs; /* R: number of records */ + size_t bt_reclen; /* R: fixed record length */ + u_char bt_bval; /* R: delimiting byte/pad character */ + +/* + * NB: + * B_NODUPS and R_RECNO are stored on disk, and may not be changed. + */ +#define B_INMEM 0x00001 /* in-memory tree */ +#define B_METADIRTY 0x00002 /* need to write metadata */ +#define B_MODIFIED 0x00004 /* tree modified */ +#define B_NEEDSWAP 0x00008 /* if byte order requires swapping */ +#define B_RDONLY 0x00010 /* read-only tree */ + +#define B_NODUPS 0x00020 /* no duplicate keys permitted */ +#define R_RECNO 0x00080 /* record oriented tree */ + +#define R_CLOSEFP 0x00040 /* opened a file pointer */ +#define R_EOF 0x00100 /* end of input file reached. */ +#define R_FIXLEN 0x00200 /* fixed length records */ +#define R_MEMMAPPED 0x00400 /* memory mapped file. */ +#define R_INMEM 0x00800 /* in-memory file */ +#define R_MODIFIED 0x01000 /* modified file */ +#define R_RDONLY 0x02000 /* read-only file */ + +#define B_DB_LOCK 0x04000 /* DB_LOCK specified. */ +#define B_DB_SHMEM 0x08000 /* DB_SHMEM specified. */ +#define B_DB_TXN 0x10000 /* DB_TXN specified. */ + u_int32_t flags; +} BTREE; + +#include "extern.h" diff --git a/src/util/db2/btree/extern.h b/src/util/db2/btree/extern.h new file mode 100644 index 000000000..9da8486e3 --- /dev/null +++ b/src/util/db2/btree/extern.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)extern.h 8.11 (Berkeley) 1/9/95 + */ + +int __bt_close __P((DB *)); +int __bt_cmp __P((BTREE *, const DBT *, EPG *)); +int __bt_crsrdel __P((BTREE *, EPGNO *)); +int __bt_defcmp __P((const DBT *, const DBT *)); +size_t __bt_defpfx __P((const DBT *, const DBT *)); +int __bt_delete __P((const DB *, const DBT *, u_int)); +int __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int)); +int __bt_fd __P((const DB *)); +int __bt_free __P((BTREE *, PAGE *)); +int __bt_get __P((const DB *, const DBT *, DBT *, u_int)); +PAGE *__bt_new __P((BTREE *, db_pgno_t *)); +void __bt_pgin __P((void *, db_pgno_t, void *)); +void __bt_pgout __P((void *, db_pgno_t, void *)); +int __bt_push __P((BTREE *, db_pgno_t, int)); +int __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int)); +int __bt_ret __P((BTREE *, EPG *, DBT *, DBT *, DBT *, DBT *, int)); +EPG *__bt_search __P((BTREE *, const DBT *, int *)); +int __bt_seq __P((const DB *, DBT *, DBT *, u_int)); +void __bt_setcur __P((BTREE *, db_pgno_t, u_int)); +int __bt_split __P((BTREE *, PAGE *, + const DBT *, const DBT *, int, size_t, u_int32_t)); +int __bt_sync __P((const DB *, u_int)); + +int __ovfl_delete __P((BTREE *, void *)); +int __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *)); +int __ovfl_put __P((BTREE *, const DBT *, db_pgno_t *)); + +#ifdef DEBUG +int __bt_dnpage __P((DB *, db_pgno_t)); +int __bt_dpage __P((PAGE *)); +int __bt_dump __P((DB *)); +#endif +#ifdef STATISTICS +int __bt_stat __P((DB *)); +#endif diff --git a/src/util/db2/clib/memmove.c b/src/util/db2/clib/memmove.c new file mode 100644 index 000000000..e8384f7c8 --- /dev/null +++ b/src/util/db2/clib/memmove.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)bcopy.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/src/util/db2/clib/mkstemp.c b/src/util/db2/clib/mkstemp.c new file mode 100644 index 000000000..07d4186a9 --- /dev/null +++ b/src/util/db2/clib/mkstemp.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include + +static int _gettemp(); + +mkstemp(path) + char *path; +{ + int fd; + + return (_gettemp(path, &fd) ? fd : -1); +} + +static +_gettemp(path, doopen) + char *path; + register int *doopen; +{ + extern int errno; + register char *start, *trv; + struct stat sbuf; + u_int pid; + + pid = getpid(); + for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ + while (*--trv == 'X') { + *trv = (pid % 10) + '0'; + pid /= 10; + } + + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + if (stat(path, &sbuf)) + return(0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return(0); + } + *trv = '/'; + break; + } + } + + for (;;) { + if (doopen) { + if ((*doopen = + open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) + return(1); + if (errno != EEXIST) + return(0); + } + else if (stat(path, &sbuf)) + return(errno == ENOENT ? 1 : 0); + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) + return(0); + if (*trv == 'z') + *trv++ = 'a'; + else { + if (isdigit(*trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /*NOTREACHED*/ +} diff --git a/src/util/db2/clib/strerror.c b/src/util/db2/clib/strerror.c new file mode 100644 index 000000000..53f374bdf --- /dev/null +++ b/src/util/db2/clib/strerror.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strerror.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include + +char * +strerror(num) + int num; +{ + extern int sys_nerr; + extern char *sys_errlist[]; +#define UPREFIX "Unknown error: " + static char ebuf[40] = UPREFIX; /* 64-bit number + slop */ + register unsigned int errnum; + register char *p, *t; + char tmp[40]; + + errnum = num; /* convert to unsigned */ + if (errnum < sys_nerr) + return(sys_errlist[errnum]); + + /* Do this by hand, so we don't include stdio(3). */ + t = tmp; + do { + *t++ = "0123456789"[errnum % 10]; + } while (errnum /= 10); + for (p = ebuf + sizeof(UPREFIX) - 1;;) { + *p++ = *--t; + if (t <= tmp) + break; + } + return(ebuf); +} diff --git a/src/util/db2/configure.in b/src/util/db2/configure.in new file mode 100644 index 000000000..3d256c40b --- /dev/null +++ b/src/util/db2/configure.in @@ -0,0 +1,63 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(db/db.c) +AC_CONFIG_HEADER(obj/db-config.h) +dnl checks for programs +AC_PROG_CC +AC_PROG_RANLIB + +AC_PATH_PROG(FALSE,false,:) +AC_PATH_PROG(SH,sh,$FALSE) +AC_PATH_PROG(SH5,sh5,$FALSE) +AC_PATH_PROG(BASH,bash,$FALSE) + +AC_CACHE_CHECK([checking for shell with functions],local_cv_program_fctsh, +[if $SH -c 'foo() { true; }; foo' > /dev/null 2>&1; then + local_cv_program_fctsh=$SH +else + if $SH5 -c 'foo() { true; }; foo' > /dev/null 2>&1; then + local_cv_program_fctsh=$SH5 + else + if $BASH -c 'foo() { true; }; foo' > /dev/null 2>&1; then + local_cv_program_fctsh=$BASH + else + local_cv_program_fctsh=$FALSE + fi + fi +fi]) + +FCTSH=$local_cv_program_fctsh +AC_SUBST(FCTSH) + +dnl checks for libraries +dnl checks for header files +AC_CHECK_HEADERS(unistd.h) +dnl checks for typedefs +AC_TYPE_SIZE_T + +AC_CHECK_TYPE(ssize_t, int) + +AC_CHECK_TYPE(u_char, unsigned char) +AC_CHECK_TYPE(u_short, unsigned short) +AC_CHECK_TYPE(u_int, unsigned int) +AC_CHECK_TYPE(u_long, unsigned long) + +AC_CHECK_TYPE(int8_t, signed char) +AC_CHECK_TYPE(u_int8_t, unsigned char) +AC_CHECK_TYPE(int16_t, short) +AC_CHECK_TYPE(u_int16_t, unsigned short) +AC_CHECK_TYPE(int32_t, int) +AC_CHECK_TYPE(u_int32_t, unsigned int) +dnl checks for structures +dnl checks for compiler characteristics +AC_C_BIGENDIAN +AC_C_CONST +dnl checks for library functions +AC_CHECK_FUNC(memmove,,MEMMOVE_OBJ=memmove.o) +AC_CHECK_FUNC(mkstemp,,MKSTEMP_OBJ=mkstemp.o) +AC_CHECK_FUNC(strerror,,STRERROR_OBJ=strerror.o) +AC_SUBST(MEMMOVE_OBJ) +AC_SUBST(MKSTEMP_OBJ) +AC_SUBST(STRERROR_OBJ) +AC_CHECK_FUNCS(memmove mkstemp strerror) +dnl checks for system services +AC_OUTPUT(Makefile obj/Makefile) diff --git a/src/util/db2/db/Makefile.inc b/src/util/db2/db/Makefile.inc new file mode 100644 index 000000000..59478ba19 --- /dev/null +++ b/src/util/db2/db/Makefile.inc @@ -0,0 +1,5 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/db + +SRCS+= db.c diff --git a/src/util/db2/db/db.c b/src/util/db2/db/db.c new file mode 100644 index 000000000..dc38abdc4 --- /dev/null +++ b/src/util/db2/db/db.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)db.c 8.4 (Berkeley) 2/21/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" + +DB * +dbopen(fname, flags, mode, type, openinfo) + const char *fname; + int flags, mode; + DBTYPE type; + const void *openinfo; +{ + +#define DB_FLAGS (DB_LOCK | DB_SHMEM | DB_TXN) +#define USE_OPEN_FLAGS \ + (O_CREAT | O_EXCL | O_EXLOCK | O_NONBLOCK | O_RDONLY | \ + O_RDWR | O_SHLOCK | O_TRUNC) + + if ((flags & ~(USE_OPEN_FLAGS | DB_FLAGS)) == 0) + switch (type) { + case DB_BTREE: + return (__bt_open(fname, flags & USE_OPEN_FLAGS, + mode, openinfo, flags & DB_FLAGS)); + case DB_HASH: + return (__hash_open(fname, flags & USE_OPEN_FLAGS, + mode, openinfo, flags & DB_FLAGS)); + case DB_RECNO: + return (__rec_open(fname, flags & USE_OPEN_FLAGS, + mode, openinfo, flags & DB_FLAGS)); + } + errno = EINVAL; + return (NULL); +} + +static int +__dberr() +{ + return (RET_ERROR); +} + +/* + * __DBPANIC -- Stop. + * + * Parameters: + * dbp: pointer to the DB structure. + */ +void +__dbpanic(dbp) + DB *dbp; +{ + /* The only thing that can succeed is a close. */ + dbp->del = (int (*)())__dberr; + dbp->fd = (int (*)())__dberr; + dbp->get = (int (*)())__dberr; + dbp->put = (int (*)())__dberr; + dbp->seq = (int (*)())__dberr; + dbp->sync = (int (*)())__dberr; +} diff --git a/src/util/db2/docs/btree.3.ps b/src/util/db2/docs/btree.3.ps new file mode 100644 index 000000000..c79c97232 --- /dev/null +++ b/src/util/db2/docs/btree.3.ps @@ -0,0 +1,366 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 132.34(BTREE\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 132.34(anual BTREE\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 84 S +(ME).18 E F0(btree \255 btree database access method)108 96 Q F1(SYNOPSIS)72 +112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 .198(The routine)108 165.6 R +/F3 10/Times-Italic@0 SF(dbopen)2.698 E F0 .198(is the library interf)2.698 F +.198(ace to database \214les.)-.1 F .198 +(One of the supported \214le formats is btree \214les.)5.198 F .974 +(The general description of the database access methods is in)108 177.6 R F3 +(dbopen)3.475 E F0 .975(\(3\), this manual page describes only).24 F +(the btree speci\214c information.)108 189.6 Q(The btree data structure is a s\ +orted, balanced tree structure storing associated k)108 206.4 Q -.15(ey)-.1 G +(/data pairs.).15 E .504(The btree access method speci\214c data structure pro) +108 223.2 R .504(vided to)-.15 F F3(dbopen)3.004 E F0 .503 +(is de\214ned in the include \214le as)-.4 F(follo)108 +235.2 Q(ws:)-.25 E(typedef struct {)108 252 Q(u_long \215ags;)144 264 Q +(u_int cachesize;)144 276 Q(inde)144 288 Q(x_t psize;)-.15 E(int lorder;)144 +300 Q(int mink)144 312 Q -.15(ey)-.1 G(page;).15 E +(int \(*compare\)\(const DBT *k)144 324 Q -.15(ey)-.1 G(1, const DBT *k).15 E +-.15(ey)-.1 G(2\);).15 E(int \(*pre\214x\)\(const DBT *k)144 336 Q -.15(ey)-.1 +G(1, const DBT *k).15 E -.15(ey)-.1 G(2\);).15 E 2.5(}B)108 348 S(TREEINFO;) +121.97 348 Q(The elements of this structure are as follo)108 364.8 Q(ws:)-.25 E +14.61(\215ags The)108 381.6 R(\215ag v)2.5 E(alue is speci\214ed by)-.25 E F3 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)313.2 381.6 S(he follo)321.81 +381.6 Q(wing v)-.25 E(alues:)-.25 E(R_DUP)144 398.4 Q 1.296(Permit duplicate k) +180 410.4 R -.15(ey)-.1 G 3.796(si).15 G 3.796(nt)275.578 410.4 S 1.296 +(he tree, i.e. permit insertion if the k)287.154 410.4 R 1.596 -.15(ey t)-.1 H +3.796(ob).15 G 3.796(ei)466.878 410.4 S 1.296(nserted already)477.894 410.4 R +-.15(ex)180 422.4 S 1.935(ists in the tree.).15 F 1.935(The def)6.935 F 1.935 +(ault beha)-.1 F(vior)-.2 E 4.435(,a)-.4 G 4.435(sd)358.215 422.4 S 1.935 +(escribed in)371.54 422.4 R F3(dbopen)4.435 E F0 1.935(\(3\), is to o).24 F +-.15(ve)-.15 G 1.935(rwrite a).15 F .148(matching k)180 434.4 R .448 -.15(ey w) +-.1 H .148(hen inserting a ne).15 F 2.649(wk)-.25 G .449 -.15(ey o)329.709 +434.4 T 2.649(rt).15 G 2.649(of)355.407 434.4 S .149(ail if the R_NOO)366.286 +434.4 R(VER)-.5 E .149(WRITE \215ag is speci-)-.55 F 5.972(\214ed. The)180 +446.4 R 3.472(R_DUP \215ag is o)5.972 F -.15(ve)-.15 G 3.472 +(rridden by the R_NOO).15 F(VER)-.5 E 3.471(WRITE \215ag, and if the)-.55 F +(R_NOO)180 458.4 Q(VER)-.5 E .781 +(WRITE \215ag is speci\214ed, attempts to insert duplicate k)-.55 F -.15(ey)-.1 +G 3.282(si).15 G .782(nto the tree will)474.604 458.4 R -.1(fa)180 470.4 S(il.) +.1 E 1.13(If the database contains duplicate k)180 487.2 R -.15(ey)-.1 G 1.129 +(s, the order of retrie).15 F -.25(va)-.25 G 3.629(lo).25 G 3.629(fk)439.644 +487.2 S -.15(ey)451.503 487.2 S 1.129(/data pairs is unde-).15 F .837 +(\214ned if the)180 499.2 R F3 -.1(ge)3.337 G(t).1 E F0 .837 +(routine is used, ho)3.337 F(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G F3(seq)3.737 +E F0 .838(routine calls with the R_CURSOR \215ag set)3.337 F(will al)180 511.2 +Q -.1(wa)-.1 G(ys return the logical `).1 E(`\214rst')-.74 E 2.5('o)-.74 G 2.5 +(fa)333.85 511.2 S .3 -.15(ny g)344.12 511.2 T(roup of duplicate k).15 E -.15 +(ey)-.1 G(s.).15 E(cachesize)108 528 Q 3.056(As)144 540 S .556 +(uggested maximum size \(in bytes\) of the memory cache.)158.166 540 R .555 +(This v)5.556 F .555(alue is)-.25 F F2(only)3.055 E F0(advisory)3.055 E 3.055 +(,a)-.65 G .555(nd the)514.725 540 R .759 +(access method will allocate more memory rather than f)144 552 R 3.259 +(ail. Since)-.1 F -2.15 -.25(ev e)3.259 H .76(ry search e).25 F .76 +(xamines the root)-.15 F .055 +(page of the tree, caching the most recently used pages substantially impro)144 +564 R -.15(ve)-.15 G 2.554(sa).15 G .054(ccess time.)459.578 564 R .054 +(In addi-)5.054 F .661(tion, ph)144 576 R .662(ysical writes are delayed as lo\ +ng as possible, so a moderate cache can reduce the number)-.05 F .601 +(of I/O operations signi\214cantly)144 588 R 5.601(.O)-.65 G -.15(bv)280.744 +588 S(iously).15 E 3.101(,u)-.65 G .601(sing a cache increases \(b)324.995 588 +R .6(ut only increases\) the lik)-.2 F(eli-)-.1 E .19(hood of corruption or lo\ +st data if the system crashes while a tree is being modi\214ed.)144 600 R(If) +5.191 E F3(cac)2.691 E(hesize)-.15 E F0(is)2.691 E 2.5(0\()144 612 S +(no size is speci\214ed\) a def)154.83 612 Q(ault cache is used.)-.1 E 12.95 +(psize P)108 628.8 R .45 +(age size is the size \(in bytes\) of the pages used for nodes in the tree.) +-.15 F .449(The minimum page size is)5.449 F .442 +(512 bytes and the maximum page size is 64K.)144 640.8 R(If)5.442 E F3(psize) +2.942 E F0 .442(is 0 \(no page size is speci\214ed\) a page size)2.942 F +(is chosen based on the underlying \214le system I/O block size.)144 652.8 Q +9.62(lorder The)108 669.6 R 1.597(byte order for inte)4.097 F 1.596 +(gers in the stored database metadata.)-.15 F 1.596 +(The number should represent the)6.596 F .688(order as an inte)144 681.6 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E F3(lor)3.189 E(der)-.37 E F0 .689 +(is 0 \(no)3.189 F(order is speci\214ed\) the current host order is used.)144 +693.6 Q 174.135(4.4BSD June)72 732 R(4, 1993)2.5 E(1)535 732 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 132.34(BTREE\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 132.34(anual BTREE\(3\))340.17 48 R(mink)108 84 Q -.15(ey)-.1 G(page).15 +E 1.423(The minimum number of k)144 96 R -.15(ey)-.1 G 3.923(sw).15 G 1.422 +(hich will be stored on an)282.245 96 R 3.922(ys)-.15 G 1.422(ingle page.) +400.618 96 R 1.422(This v)6.422 F 1.422(alue is used to)-.25 F .257 +(determine which k)144 108 R -.15(ey)-.1 G 2.757(sw).15 G .257 +(ill be stored on o)242.001 108 R -.15(ve)-.15 G(r\215o).15 E 2.757(wp)-.25 G +.257(ages, i.e. if a k)348.006 108 R .558 -.15(ey o)-.1 H 2.758(rd).15 G .258 +(ata item is longer than the)435.11 108 R 1.102(pagesize di)144 120 R 1.102 +(vided by the mink)-.25 F -.15(ey)-.1 G 1.102(page v).15 F 1.102 +(alue, it will be stored on o)-.25 F -.15(ve)-.15 G(r\215o).15 E 3.602(wp)-.25 +G 1.102(ages instead of in the)451.164 120 R(page itself.)144 132 Q(If)5 E/F1 +10/Times-Italic@0 SF(mink)2.5 E -.3(ey)-.1 G(pa).3 E -.1(ge)-.1 G F0 +(is 0 \(no minimum number of k)2.6 E -.15(ey)-.1 G 2.5(si).15 G 2.5(ss)392.84 +132 S(peci\214ed\) a v)403.12 132 Q(alue of 2 is used.)-.25 E(compare)108 148.8 +Q .751(Compare is the k)144 160.8 R 1.051 -.15(ey c)-.1 H .751 +(omparison function.).15 F .751(It must return an inte)5.751 F .752 +(ger less than, equal to, or greater)-.15 F .913(than zero if the \214rst k)144 +172.8 R 1.213 -.15(ey a)-.1 H -.18(rg).15 G .913 +(ument is considered to be respecti).18 F -.15(ve)-.25 G .913 +(ly less than, equal to, or greater).15 F .352(than the second k)144 184.8 R +.652 -.15(ey a)-.1 H -.18(rg).15 G 2.852(ument. The).18 F .353 +(same comparison function must be used on a gi)2.852 F -.15(ve)-.25 G 2.853(nt) +.15 G .353(ree e)503.127 184.8 R -.15(ve)-.25 G(ry).15 E .817 +(time it is opened.)144 196.8 R(If)5.817 E F1(compar)3.317 E(e)-.37 E F0 .817 +(is NULL \(no comparison function is speci\214ed\), the k)3.317 F -.15(ey)-.1 G +3.316(sa).15 G .816(re com-)508.364 196.8 R(pared le)144 208.8 Q(xically)-.15 E +2.5(,w)-.65 G(ith shorter k)214.57 208.8 Q -.15(ey)-.1 G 2.5(sc).15 G +(onsidered less than longer k)282.92 208.8 Q -.15(ey)-.1 G(s.).15 E 10.17 +(pre\214x Pre\214x)108 225.6 R .291(is the pre\214x comparison function.)2.791 +F .292(If speci\214ed, this routine must return the number of bytes)5.291 F +.937(of the second k)144 237.6 R 1.237 -.15(ey a)-.1 H -.18(rg).15 G .937 +(ument which are necessary to determine that it is greater than the \214rst k) +.18 F -.15(ey)-.1 G(ar)144 249.6 Q 3.477(gument. If)-.18 F .977(the k)3.477 F +-.15(ey)-.1 G 3.477(sa).15 G .977(re equal, the k)241.898 249.6 R 1.277 -.15 +(ey l)-.1 H .978(ength should be returned.).15 F .978 +(Note, the usefulness of this)5.978 F .558(routine is v)144 261.6 R .558 +(ery data dependent, b)-.15 F .558 +(ut, in some data sets can produce signi\214cantly reduced tree sizes)-.2 F +.354(and search times.)144 273.6 R(If)5.354 E F1(pr)2.854 E(e\214x)-.37 E F0 +.354(is NULL \(no pre\214x function is speci\214ed\),)2.854 F/F2 10 +/Times-Bold@0 SF(and)2.854 E F0 .354(no comparison function)2.854 F .193 +(is speci\214ed, a def)144 285.6 R .193(ault le)-.1 F .193 +(xical comparison routine is used.)-.15 F(If)5.192 E F1(pr)2.692 E(e\214x)-.37 +E F0 .192(is NULL and a comparison rou-)2.692 F +(tine is speci\214ed, no pre\214x comparison is done.)144 297.6 Q .79 +(If the \214le already e)108 314.4 R .79(xists \(and the O_TR)-.15 F .79 +(UNC \215ag is not speci\214ed\), the v)-.4 F .79 +(alues speci\214ed for the parameters)-.25 F +(\215ags, lorder and psize are ignored in f)108 326.4 Q -.2(avo)-.1 G 2.5(ro).2 +G 2.5(ft)284.4 326.4 S(he v)293.01 326.4 Q(alues used when the tree w)-.25 E +(as created.)-.1 E -.15(Fo)108 343.2 S(rw).15 E +(ard sequential scans of a tree are from the least k)-.1 E .3 -.15(ey t)-.1 H +2.5(ot).15 G(he greatest.)348.55 343.2 Q 1.043(Space freed up by deleting k)108 +360 R -.15(ey)-.1 G 1.043(/data pairs from the tree is ne).15 F -.15(ve)-.25 G +3.543(rr).15 G 1.043(eclaimed, although it is normally made)378.686 360 R -.2 +(av)108 372 S 1.394(ailable for reuse.)-.05 F 1.394 +(This means that the btree storage structure is gro)6.394 F(w-only)-.25 E 6.395 +(.T)-.65 G 1.395(he only solutions are to)441.09 372 R -.2(avo)108 384 S(id e) +.2 E(xcessi)-.15 E .3 -.15(ve d)-.25 H +(eletions, or to create a fresh tree periodically from a scan of an e).15 E +(xisting one.)-.15 E .344(Searches, insertions, and deletions in a btree will \ +all complete in O lg base N where base is the a)108 400.8 R -.15(ve)-.2 G .343 +(rage \214ll).15 F -.1(fa)108 412.8 S(ctor).1 E 5.798(.O)-.55 G .799 +(ften, inserting ordered data into btrees results in a lo)146.188 412.8 R 3.299 +<778c>-.25 G .799(ll f)377.505 412.8 R(actor)-.1 E 5.799(.T)-.55 G .799 +(his implementation has been)423.443 412.8 R(modi\214ed to mak)108 424.8 Q 2.5 +(eo)-.1 G(rdered insertion the best case, resulting in a much better than norm\ +al page \214ll f)185.4 424.8 Q(actor)-.1 E(.)-.55 E/F3 9/Times-Bold@0 SF +(SEE ALSO)72 441.6 Q F1(dbopen)108 453.6 Q F0(\(3\),).24 E F1(hash)2.5 E F0 +(\(3\),).28 E F1(mpool)2.5 E F0(\(3\),).51 E F1 -.37(re)2.5 G(cno).37 E F0 +(\(3\)).18 E F1(The Ubiquitous B-tr)108 477.6 Q(ee)-.37 E F0 2.5(,D).18 G +(ouglas Comer)209.47 477.6 Q 2.5(,A)-.4 G(CM Comput. Surv)276.72 477.6 Q 2.5 +(.1)-.65 G(1, 2 \(June 1979\), 121-138.)360.25 477.6 Q F1(Pr)108 501.6 Q 1.588 +(e\214x B-tr)-.37 F(ees)-.37 E F0 4.088(,B).27 G 1.587(ayer and Unterauer) +177.636 501.6 R 4.087(,A)-.4 G 1.587(CM T)270.447 501.6 R 1.587 +(ransactions on Database Systems, V)-.35 F 1.587(ol. 2, 1 \(March 1977\),)-1.29 +F(11-26.)108 513.6 Q F1(The Art of Computer Pr)108 537.6 Q -.1(og)-.45 G -.15 +(ra).1 G(mming V).15 E(ol. 3: Sorting and Sear)-1.11 E -.15(ch)-.37 G(ing).15 E +F0 2.5(,D).22 G(.E. Knuth, 1968, pp 471-480.)382 537.6 Q F3 -.09(BU)72 554.4 S +(GS).09 E F0(Only big and little endian byte order is supported.)108 566.4 Q +174.135(4.4BSD June)72 732 R(4, 1993)2.5 E(2)535 732 Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/docs/dbopen.3.ps b/src/util/db2/docs/dbopen.3.ps new file mode 100644 index 000000000..c621bef97 --- /dev/null +++ b/src/util/db2/docs/dbopen.3.ps @@ -0,0 +1,508 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 4 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.01(DBOPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.01(anual DBOPEN\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 +84 S(ME).18 E F0(dbopen \255 database access methods)108 96 Q F1(SYNOPSIS)72 +112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q +(#include )108 136.8 Q(#include )-.4 E(DB *)108 +172.8 Q(dbopen\(const char *\214le, int \215ags, int mode, DBTYPE type,)108 +184.8 Q(const v)158 196.8 Q(oid *openinf)-.1 E(o\);)-.25 E F1(DESCRIPTION)72 +213.6 Q/F3 10/Times-Italic@0 SF(Dbopen)108 225.6 Q F0 .032 +(is the library interf)2.532 F .031(ace to database \214les.)-.1 F .031 +(The supported \214le formats are btree, hashed and UNIX \214le)5.031 F 2.82 +(oriented. The)108 237.6 R .32 +(btree format is a representation of a sorted, balanced tree structure.)2.82 F +.321(The hashed format is an)5.321 F -.15(ex)108 249.6 S .424 +(tensible, dynamic hashing scheme.).15 F .423 +(The \215at-\214le format is a byte stream \214le with \214x)5.423 F .423 +(ed or v)-.15 F .423(ariable length)-.25 F 2.906(records. The)108 261.6 R .407 +(formats and \214le format speci\214c information are described in detail in t\ +heir respecti)2.906 F .707 -.15(ve m)-.25 H(anual).15 E(pages)108 273.6 Q F3 +(btr)2.5 E(ee)-.37 E F0(\(3\),).18 E F3(hash)2.5 E F0(\(3\) and).28 E F3 -.37 +(re)2.5 G(cno).37 E F0(\(3\).).18 E .433(Dbopen opens)108 290.4 R F3(\214le) +2.933 E F0 .433(for reading and/or writing.)2.933 F .433(Files ne)5.433 F -.15 +(ve)-.25 G 2.933(ri).15 G .433(ntended to be preserv)346.737 290.4 R .433 +(ed on disk may be created)-.15 F(by setting the \214le parameter to NULL.)108 +302.4 Q(The)108 319.2 Q F3<8d61>4.661 E(gs)-.1 E F0(and)4.661 E F3 2.161 +(mode ar)4.661 F(guments)-.37 E F0 2.161(are as speci\214ed to the)4.661 F F3 +(open)4.661 E F0 2.162(\(2\) routine, ho).24 F(we)-.25 E -.15(ve)-.25 G 2.962 +-.4(r, o).15 H 2.162(nly the O_CREA).4 F -.74(T,)-1.11 G .128 +(O_EXCL, O_EXLOCK, O_NONBLOCK, O_RDONL)108 331.2 R 2.708 -1.29(Y, O)-1 H(_RD) +1.29 E .128(WR, O_SHLOCK and O_TR)-.3 F .127(UNC \215ags are)-.4 F 2.5 +(meaningful. \(Note,)108 343.2 R(opening a database \214le O_WR)2.5 E(ONL)-.4 E +2.5(Yi)-1 G 2.5(sn)342.67 343.2 S(ot possible.\))354.06 343.2 Q(The)108 360 Q +F3(type)5.337 E F0(ar)5.337 E 2.837 +(gument is of type DBTYPE \(as de\214ned in the include \214le\) and may be set to)-.4 F(DB_BTREE, DB_HASH or DB_RECNO.) +108 372 Q(The)108 388.8 Q F3(openinfo)2.85 E F0(ar)2.85 E .349(gument is a poi\ +nter to an access method speci\214c structure described in the access method') +-.18 F(s)-.55 E .03(manual page.)108 400.8 R(If)5.03 E F3(openinfo)2.53 E F0 +.031(is NULL, each access method will use def)2.53 F .031 +(aults appropriate for the system and the)-.1 F(access method.)108 412.8 Q F3 +(Dbopen)108 429.6 Q F0 .416 +(returns a pointer to a DB structure on success and NULL on error)2.917 F 5.416 +(.T)-.55 G .416(he DB structure is de\214ned in)423.21 429.6 R(the include \214le, and contains at least the follo)-.4 E +(wing \214elds:)-.25 E(typedef struct {)108 465.6 Q(DBTYPE type;)144 477.6 Q +(int \(*close\)\(const DB *db\);)144 489.6 Q +(int \(*del\)\(const DB *db, const DBT *k)144 501.6 Q -.15(ey)-.1 G 2.5(,u)-.5 +G(_int \215ags\);)318.92 501.6 Q(int \(*fd\)\(const DB *db\);)144 513.6 Q +(int \(*get\)\(const DB *db, DBT *k)144 525.6 Q -.15(ey)-.1 G 2.5(,D)-.5 G +(BT *data, u_int \215ags\);)297.53 525.6 Q(int \(*put\)\(const DB *db, DBT *k) +144 537.6 Q -.15(ey)-.1 G 2.5(,c)-.5 G(onst DBT *data,)295.31 537.6 Q +(u_int \215ags\);)194 549.6 Q(int \(*sync\)\(const DB *db, u_int \215ags\);)144 +561.6 Q(int \(*seq\)\(const DB *db, DBT *k)144 573.6 Q -.15(ey)-.1 G 2.5(,D)-.5 +G(BT *data, u_int \215ags\);)298.64 573.6 Q 2.5(}D)108 585.6 S(B;)122.52 585.6 +Q .101 +(These elements describe a database type and a set of functions performing v) +108 602.4 R .101(arious actions.)-.25 F .101(These functions)5.101 F(tak)108 +614.4 Q 3.039(eap)-.1 G .539(ointer to a structure as returned by)140.078 614.4 +R F3(dbopen)3.038 E F0 3.038(,a).24 G .538 +(nd sometimes one or more pointers to k)323.196 614.4 R -.15(ey)-.1 G .538 +(/data struc-).15 F(tures and a \215ag v)108 626.4 Q(alue.)-.25 E 16.28 +(type The)108 643.2 R +(type of the underlying access method \(and \214le format\).)2.5 E 12.95 +(close A)108 660 R .988(pointer to a routine to \215ush an)3.488 F 3.489(yc) +-.15 G .989(ached information to disk, free an)293.968 660 R 3.489(ya)-.15 G +.989(llocated resources, and)446.662 660 R .112 +(close the underlying \214le\(s\).)144 672 R .111(Since k)5.112 F -.15(ey)-.1 G +.111(/data pairs may be cached in memory).15 F 2.611(,f)-.65 G .111 +(ailing to sync the \214le)455.666 672 R .494(with a)144 684 R F3(close)2.994 E +F0(or)2.994 E F3(sync)2.994 E F0 .495 +(function may result in inconsistent or lost information.)2.994 F F3(Close) +5.495 E F0 .495(routines return)2.995 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 +G(istrib)132.57 732 Q 89.875(ution September)-.2 F(13, 1993)2.5 E(1)535 732 Q +EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.01(DBOPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.01(anual DBOPEN\(3\))340.17 48 R(-1 on error \(setting)144 84 Q +/F1 10/Times-Italic@0 SF(errno)2.5 E F0 2.5(\)a).18 G(nd 0 on success.)254.43 +84 Q 21.28(del A)108 100.8 R(pointer to a routine to remo)2.5 E .3 -.15(ve k) +-.15 H -.15(ey).05 G(/data pairs from the database.).15 E(The parameter)144 +117.6 Q F1<8d61>2.5 E(g)-.1 E F0(may be set to the follo)2.5 E(wing v)-.25 E +(alue:)-.25 E(R_CURSOR)144 134.4 Q .289 +(Delete the record referenced by the cursor)180 146.4 R 5.288(.T)-.55 G .288 +(he cursor must ha)363.342 146.4 R .588 -.15(ve p)-.2 H(re).15 E .288 +(viously been initial-)-.25 F(ized.)180 158.4 Q F1(Delete)144 175.2 Q F0 .03 +(routines return -1 on error \(setting)2.53 F F1(errno)2.53 E F0 .03 +(\), 0 on success, and 1 if the speci\214ed).18 F F1 -.1(ke)2.53 G(y)-.2 E F0 +-.1(wa)2.53 G 2.53(sn).1 G .03(ot in)521.91 175.2 R(the \214le.)144 187.2 Q +25.17(fd A)108 204 R .451 +(pointer to a routine which returns a \214le descriptor representati)2.951 F +.75 -.15(ve o)-.25 H 2.95(ft).15 G .45(he underlying database.)431.73 204 R(A) +5.45 E .942(\214le descriptor referencing the same \214le will be returned to \ +all processes which call)144 216 R F1(dbopen)3.442 E F0(with)3.442 E 1.629 +(the same)144 228 R F1(\214le)4.129 E F0 4.129(name. This)4.129 F 1.628 +(\214le descriptor may be safely used as a ar)4.128 F 1.628(gument to the)-.18 +F F1(fcntl)4.128 E F0 1.628(\(2\) and).51 F F1(\215oc)144 240 Q(k)-.2 E F0 .425 +(\(2\) locking functions.).67 F .425 +(The \214le descriptor is not necessarily associated with an)5.425 F 2.925(yo) +-.15 G 2.925(ft)492.7 240 S .425(he under)501.735 240 R(-)-.2 E .198 +(lying \214les used by the access method.)144 252 R .198 +(No \214le descriptor is a)5.198 F -.25(va)-.2 G .198 +(ilable for in memory databases.).25 F F1(Fd)5.198 E F0 +(routines return -1 on error \(setting)144 264 Q F1(errno)2.5 E F0 +(\), and the \214le descriptor on success.).18 E 21.28(get A)108 280.8 R +(pointer to a routine which is the interf)2.5 E .001(ace for k)-.1 F -.15(ey) +-.1 G .001(ed retrie).15 F -.25(va)-.25 G 2.501(lf).25 G .001 +(rom the database.)399.755 280.8 R .001(The address and)5.001 F .061 +(length of the data associated with the speci\214ed)144 292.8 R F1 -.1(ke)2.561 +G(y)-.2 E F0 .06(are returned in the structure referenced by)2.561 F F1(data) +2.56 E F0(.).26 E F1(Get)144 304.8 Q F0(routines return -1 on error \(setting) +2.5 E F1(errno)2.5 E F0(\), 0 on success, and 1 if the).18 E F1 -.1(ke)2.5 G(y) +-.2 E F0 -.1(wa)2.5 G 2.5(sn).1 G(ot in the \214le.)471.66 304.8 Q 20.72(put A) +108 321.6 R(pointer to a routine to store k)2.5 E -.15(ey)-.1 G +(/data pairs in the database.).15 E(The parameter)144 338.4 Q F1<8d61>2.5 E(g) +-.1 E F0(may be set to one of the follo)2.5 E(wing v)-.25 E(alues:)-.25 E +(R_CURSOR)144 355.2 Q .051(Replace the k)180 367.2 R -.15(ey)-.1 G .051 +(/data pair referenced by the cursor).15 F 5.052(.T)-.55 G .052 +(he cursor must ha)393.98 367.2 R .352 -.15(ve p)-.2 H(re).15 E .052 +(viously been)-.25 F(initialized.)180 379.2 Q(R_IAFTER)144 396 Q 1.165 +(Append the data immediately after the data referenced by)180 408 R F1 -.1(ke) +3.664 G(y)-.2 E F0 3.664(,c).32 G 1.164(reating a ne)446.758 408 R 3.664(wk) +-.25 G -.15(ey)511.27 408 S(/data).15 E(pair)180 420 Q 6.065(.T)-.55 G 1.065 +(he record number of the appended k)209.675 420 R -.15(ey)-.1 G 1.065 +(/data pair is returned in the).15 F F1 -.1(ke)3.565 G(y)-.2 E F0(structure.) +3.565 E(\(Applicable only to the DB_RECNO access method.\))180 432 Q(R_IBEFORE) +144 448.8 Q 1.293(Insert the data immediately before the data referenced by)180 +460.8 R F1 -.1(ke)3.793 G(y)-.2 E F0 3.793(,c).32 G 1.293(reating a ne)446.371 +460.8 R 3.793(wk)-.25 G -.15(ey)511.27 460.8 S(/data).15 E(pair)180 472.8 Q +6.54(.T)-.55 G 1.54(he record number of the inserted k)210.15 472.8 R -.15(ey) +-.1 G 1.541(/data pair is returned in the).15 F F1 -.1(ke)4.041 G(y)-.2 E F0 +(structure.)4.041 E(\(Applicable only to the DB_RECNO access method.\))180 +484.8 Q(R_NOO)144 501.6 Q(VER)-.5 E(WRITE)-.55 E(Enter the ne)180 513.6 Q 2.5 +(wk)-.25 G -.15(ey)242.69 513.6 S(/data pair only if the k).15 E .3 -.15(ey d) +-.1 H(oes not pre).15 E(viously e)-.25 E(xist.)-.15 E(R_SETCURSOR)144 530.4 Q +1.36(Store the k)180 542.4 R -.15(ey)-.1 G 1.36(/data pair).15 F 3.86(,s)-.4 G +1.359(etting or initializing the position of the cursor to reference it.)283.94 +542.4 R(\(Applicable only to the DB_BTREE and DB_RECNO access methods.\))180 +554.4 Q .563(R_SETCURSOR is a)144 571.2 R -.25(va)-.2 G .564 +(ilable only for the DB_BTREE and DB_RECNO access methods because).25 F +(it implies that the k)144 583.2 Q -.15(ey)-.1 G 2.5(sh).15 G -2.25 -.2(av e) +241.81 583.2 T(an inherent order which does not change.)2.7 E .416 +(R_IAFTER and R_IBEFORE are a)144 600 R -.25(va)-.2 G .416 +(ilable only for the DB_RECNO access method because the).25 F(y)-.15 E 1.221 +(each imply that the access method is able to create ne)144 612 R 3.722(wk)-.25 +G -.15(ey)385.644 612 S 3.722(s. This).15 F 1.222(is only true if the k)3.722 F +-.15(ey)-.1 G 3.722(sa).15 G(re)532.23 612 Q +(ordered and independent, record numbers for e)144 624 Q(xample.)-.15 E .289 +(The def)144 640.8 R .289(ault beha)-.1 F .289(vior of the)-.2 F F1(put)2.789 E +F0 .289(routines is to enter the ne)2.789 F 2.789(wk)-.25 G -.15(ey)388.998 +640.8 S .288(/data pair).15 F 2.788(,r)-.4 G .288(eplacing an)444.284 640.8 R +2.788(yp)-.15 G(re)503.03 640.8 Q(viously)-.25 E -.15(ex)144 652.8 S(isting k) +.15 E -.15(ey)-.1 G(.)-.5 E F1(Put)144 669.6 Q F0 .37 +(routines return -1 on error \(setting)2.87 F F1(errno)2.87 E F0 .37 +(\), 0 on success, and 1 if the R_NOO).18 F(VER)-.5 E(WRITE)-.55 E F1<8d61>2.87 +E(g)-.1 E F0 -.1(wa)144 681.6 S 2.5(ss).1 G(et and the k)165.84 681.6 Q .3 -.15 +(ey a)-.1 H(lready e).15 E(xists in the \214le.)-.15 E(4.4 Berk)72 732 Q(ele) +-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 89.875(ution September)-.2 F(13, 1993) +2.5 E(2)535 732 Q EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.01(DBOPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.01(anual DBOPEN\(3\))340.17 48 R 20.17(seq A)108 84 R .002 +(pointer to a routine which is the interf)2.502 F .002 +(ace for sequential retrie)-.1 F -.25(va)-.25 G 2.502(lf).25 G .002 +(rom the database.)416.694 84 R .001(The address)5.001 F .219 +(and length of the k)144 96 R .519 -.15(ey a)-.1 H .219 +(re returned in the structure referenced by).15 F/F1 10/Times-Italic@0 SF -.1 +(ke)2.72 G(y)-.2 E F0 2.72(,a).32 G .22(nd the address and length of)426.42 96 +R(the data are returned in the structure referenced by)144 108 Q F1(data)2.5 E +F0(.).26 E .937(Sequential k)144 124.8 R -.15(ey)-.1 G .937(/data pair retrie) +.15 F -.25(va)-.25 G 3.437(lm).25 G .936(ay be)289.748 124.8 R .936(gin at an) +-.15 F 3.436(yt)-.15 G .936(ime, and the position of the `)359.292 124.8 R +(`cursor')-.74 E 3.436('i)-.74 G 3.436(sn)519.894 124.8 S(ot)532.22 124.8 Q(af) +144 136.8 Q 1.585(fected by calls to the)-.25 F F1(del)4.085 E F0(,).51 E F1 +-.1(ge)4.085 G(t).1 E F0(,).68 E F1(put)4.086 E F0 4.086(,o).68 G(r)308.452 +136.8 Q F1(sync)4.086 E F0 4.086(routines. Modi\214cations)4.086 F 1.586 +(to the database during a)4.086 F 1.404(sequential scan will be re\215ected in\ + the scan, i.e. records inserted behind the cursor will not be)144 148.8 R +(returned while records inserted in front of the cursor will be returned.)144 +160.8 Q(The \215ag v)144 177.6 Q(alue)-.25 E/F2 10/Times-Bold@0 SF(must)2.5 E +F0(be set to one of the follo)2.5 E(wing v)-.25 E(alues:)-.25 E(R_CURSOR)144 +194.4 Q .523(The data associated with the speci\214ed k)180 206.4 R .824 -.15 +(ey i)-.1 H 3.024(sr).15 G 3.024(eturned. This)367.236 206.4 R(dif)3.024 E .524 +(fers from the)-.25 F F1 -.1(ge)3.024 G(t).1 E F0(routines)3.024 E 1.143 +(in that it sets or initializes the cursor to the location of the k)180 218.4 R +1.443 -.15(ey a)-.1 H 3.642(sw).15 G 3.642(ell. \(Note,)464.924 218.4 R 1.142 +(for the)3.642 F 1.275(DB_BTREE access method, the returned k)180 230.4 R 1.575 +-.15(ey i)-.1 H 3.775(sn).15 G 1.276(ot necessarily an e)386.425 230.4 R 1.276 +(xact match for the)-.15 F .598(speci\214ed k)180 242.4 R -.15(ey)-.1 G 5.598 +(.T)-.5 G .598(he returned k)246.396 242.4 R .898 -.15(ey i)-.1 H 3.098(st).15 +G .598(he smallest k)325.188 242.4 R .898 -.15(ey g)-.1 H .598 +(reater than or equal to the speci\214ed).15 F -.1(ke)180 254.4 S 1.3 -.65 +(y, p)-.05 H(ermitting partial k).65 E .3 -.15(ey m)-.1 H +(atches and range searches.\)).15 E(R_FIRST)144 271.2 Q 1.043(The \214rst k)180 +283.2 R -.15(ey)-.1 G 1.044(/data pair of the database is returned, and the cu\ +rsor is set or initialized to).15 F(reference it.)180 295.2 Q(R_LAST)144 312 Q +.085(The last k)180 324 R -.15(ey)-.1 G .085(/data pair of the database is ret\ +urned, and the cursor is set or initialized to ref-).15 F(erence it.)180 336 Q +(\(Applicable only to the DB_BTREE and DB_RECNO access methods.\))5 E(R_NEXT) +144 352.8 Q(Retrie)180 364.8 Q .604 -.15(ve t)-.25 H .304(he k).15 F -.15(ey) +-.1 G .304(/data pair immediately after the cursor).15 F 5.304(.I)-.55 G 2.804 +(ft)410.622 364.8 S .305(he cursor is not yet set, this is)419.536 364.8 R +(the same as the R_FIRST \215ag.)180 376.8 Q(R_PREV)144 393.6 Q(Retrie)180 +405.6 Q .755 -.15(ve t)-.25 H .455(he k).15 F -.15(ey)-.1 G .455 +(/data pair immediately before the cursor).15 F 5.455(.I)-.55 G 2.955(ft)419.05 +405.6 S .454(he cursor is not yet set, this)428.115 405.6 R .62 +(is the same as the R_LAST \215ag.)180 417.6 R .621 +(\(Applicable only to the DB_BTREE and DB_RECNO)5.621 F(access methods.\))180 +429.6 Q .911(R_LAST and R_PREV are a)144 446.4 R -.25(va)-.2 G .911 +(ilable only for the DB_BTREE and DB_RECNO access methods).25 F(because the)144 +458.4 Q 2.5(ye)-.15 G(ach imply that the k)202.16 458.4 Q -.15(ey)-.1 G 2.5(sh) +.15 G -2.25 -.2(av e)302.18 458.4 T(an inherent order which does not change.) +2.7 E F1(Seq)144 475.2 Q F0 .061(routines return -1 on error \(setting)2.561 F +F1(errno)2.561 E F0 .061(\), 0 on success and 1 if there are no k).18 F -.15 +(ey)-.1 G .061(/data pairs less).15 F .35 +(than or greater than the speci\214ed or current k)144 487.2 R -.15(ey)-.1 G +5.349(.I)-.5 G 2.849(ft)346.467 487.2 S .349 +(he DB_RECNO access method is being used,)355.426 487.2 R .025 +(and if the database \214le is a character special \214le and no complete k)144 +499.2 R -.15(ey)-.1 G .025(/data pairs are currently a).15 F -.25(va)-.2 G(il-) +.25 E(able, the)144 511.2 Q F1(seq)2.5 E F0(routines return 2.)2.5 E 15.17 +(sync A)108 528 R .458(pointer to a routine to \215ush an)2.958 F 2.957(yc)-.15 +G .457(ached information to disk.)289.72 528 R .457 +(If the database is in memory only)5.457 F(,)-.65 E(the)144 540 Q F1(sync)2.5 E +F0(routine has no ef)2.5 E(fect and will al)-.25 E -.1(wa)-.1 G(ys succeed.).1 +E(The \215ag v)144 556.8 Q(alue may be set to the follo)-.25 E(wing v)-.25 E +(alue:)-.25 E(R_RECNOSYNC)144 573.6 Q .077(If the DB_RECNO access method is be\ +ing used, this \215ag causes the sync routine to apply)180 585.6 R .75(to the \ +btree \214le which underlies the recno \214le, not the recno \214le itself.)180 +597.6 R .75(\(See the)5.75 F F1(bfname)3.25 E F0(\214eld of the)180 609.6 Q F1 +-.37(re)2.5 G(cno).37 E F0(\(3\) manual page for more information.\)).18 E F1 +(Sync)144 626.4 Q F0(routines return -1 on error \(setting)2.5 E F1(errno)2.5 E +F0 2.5(\)a).18 G(nd 0 on success.)336.91 626.4 Q/F3 9/Times-Bold@0 SF(KEY/D)72 +643.2 Q -1.35 -.855(AT A)-.315 H -.666(PA)3.105 G(IRS).666 E F0 .134 +(Access to all \214le types is based on k)108 655.2 R -.15(ey)-.1 G .134 +(/data pairs.).15 F .134(Both k)5.134 F -.15(ey)-.1 G 2.634(sa).15 G .134 +(nd data are represented by the follo)359.078 655.2 R .135(wing data)-.25 F +(structure:)108 667.2 Q(typedef struct {)108 684 Q(4.4 Berk)72 732 Q(ele)-.1 E +2.5(yD)-.15 G(istrib)132.57 732 Q 89.875(ution September)-.2 F(13, 1993)2.5 E +(3)535 732 Q EP +%%Page: 4 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.01(DBOPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.01(anual DBOPEN\(3\))340.17 48 R -.2(vo)144 84 S(id *data;).2 E +(size_t size;)144 96 Q 2.5(}D)108 108 S(BT)122.52 108 Q(;)-.55 E +(The elements of the DBT structure are de\214ned as follo)108 124.8 Q(ws:)-.25 +E 16.84(data A)108 141.6 R(pointer to a byte string.)2.5 E 17.95(size The)108 +158.4 R(length of the byte string.)2.5 E -2.15 -.25(Ke y)108 175.2 T .829(and \ +data byte strings may reference strings of essentially unlimited length althou\ +gh an)3.579 F 3.328(yt)-.15 G 1.028 -.1(wo o)492.894 175.2 T 3.328(ft).1 G(hem) +522.78 175.2 Q 1.133(must \214t into a)108 187.2 R -.25(va)-.2 G 1.134 +(ilable memory at the same time.).25 F 1.134 +(It should be noted that the access methods pro)6.134 F 1.134(vide no)-.15 F +(guarantees about byte string alignment.)108 199.2 Q/F1 9/Times-Bold@0 SF(ERR) +72 216 Q(ORS)-.27 E F0(The)108 228 Q/F2 10/Times-Italic@0 SF(dbopen)3.389 E F0 +.889(routine may f)3.389 F .889(ail and set)-.1 F F2(errno)3.388 E F0 .888 +(for an)3.388 F 3.388(yo)-.15 G 3.388(ft)324.376 228 S .888 +(he errors speci\214ed for the library routines)333.874 228 R F2(open)3.388 E +F0(\(2\)).24 E(and)108 240 Q F2(malloc)2.5 E F0(\(3\) or the follo).31 E(wing:) +-.25 E([EFTYPE])108 256.8 Q 2.5<418c>144 268.8 S(le is incorrectly formatted.) +159.28 268.8 Q([EINV)108 285.6 Q(AL])-1.35 E 2.812(Ap)144 297.6 S .313(aramete\ +r has been speci\214ed \(hash function, pad byte etc.\) that is incompatible w\ +ith the current)159.032 297.6 R .406 +(\214le speci\214cation or which is not meaningful for the function \(for e)144 +309.6 R .405(xample, use of the cursor with-)-.15 F .099 +(out prior initialization\) or there is a mismatch between the v)144 321.6 R .1 +(ersion number of \214le and the softw)-.15 F(are.)-.1 E(The)108 338.4 Q F2 +(close)3.469 E F0 .969(routines may f)3.469 F .969(ail and set)-.1 F F2(errno) +3.469 E F0 .969(for an)3.469 F 3.469(yo)-.15 G 3.469(ft)320.18 338.4 S .969 +(he errors speci\214ed for the library routines)329.759 338.4 R F2(close)3.468 +E F0(\(2\),).18 E F2 -.37(re)108 350.4 S(ad).37 E F0(\(2\),).77 E F2(write)2.5 +E F0(\(2\),).18 E F2(fr)2.5 E(ee)-.37 E F0(\(3\), or).18 E F2(fsync)2.5 E F0 +(\(2\).).31 E(The)108 367.2 Q F2(del)2.969 E F0(,).51 E F2 -.1(ge)2.969 G(t).1 +E F0(,).68 E F2(put)2.969 E F0(and)2.969 E F2(seq)2.969 E F0 .469 +(routines may f)2.969 F .469(ail and set)-.1 F F2(errno)2.97 E F0 .47(for an) +2.97 F 2.97(yo)-.15 G 2.97(ft)377.59 367.2 S .47 +(he errors speci\214ed for the library rou-)386.67 367.2 R(tines)108 379.2 Q F2 +-.37(re)2.5 G(ad).37 E F0(\(2\),).77 E F2(write)2.5 E F0(\(2\),).18 E F2(fr)2.5 +E(ee)-.37 E F0(\(3\) or).18 E F2(malloc)2.5 E F0(\(3\).).31 E(The)108 396 Q F2 +(fd)2.5 E F0(routines will f)2.5 E(ail and set)-.1 E F2(errno)2.5 E F0 +(to ENOENT for in memory databases.)2.5 E(The)108 412.8 Q F2(sync)2.5 E F0 +(routines may f)2.5 E(ail and set)-.1 E F2(errno)2.5 E F0(for an)2.5 E 2.5(yo) +-.15 G 2.5(ft)307.71 412.8 S(he errors speci\214ed for the library routine) +316.32 412.8 Q F2(fsync)2.5 E F0(\(2\).).31 E F1(SEE ALSO)72 429.6 Q F2(btr)108 +441.6 Q(ee)-.37 E F0(\(3\),).18 E F2(hash)2.5 E F0(\(3\),).28 E F2(mpool)2.5 E +F0(\(3\),).51 E F2 -.37(re)2.5 G(cno).37 E F0(\(3\)).18 E F2 .904(LIBTP: P)108 +465.6 R(ortable)-.8 E 3.404(,M)-.1 G .904(odular T)189.738 465.6 R -.15(ra)-.55 +G .904(nsactions for UNIX).15 F F0 3.404(,M).94 G(ar)328.884 465.6 Q .904 +(go Seltzer)-.18 F 3.403(,M)-.4 G .903(ichael Olson, USENIX proceedings,) +392.041 465.6 R -.4(Wi)108 477.6 S(nter 1992.).4 E F1 -.09(BU)72 494.4 S(GS).09 +E F0 .399(The typedef DBT is a mnemonic for `)108 506.4 R .399 +(`data base thang')-.74 F .399(', and w)-.74 F .399 +(as used because noone could think of a rea-)-.1 F(sonable name that w)108 +518.4 Q(asn')-.1 E 2.5(ta)-.18 G(lready used.)216.03 518.4 Q +(The \214le descriptor interf)108 535.2 Q +(ace is a kluge and will be deleted in a future v)-.1 E(ersion of the interf) +-.15 E(ace.)-.1 E(None of the access methods pro)108 552 Q(vide an)-.15 E 2.5 +(yf)-.15 G(orm of concurrent access, locking, or transactions.)275.16 552 Q +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 89.875 +(ution September)-.2 F(13, 1993)2.5 E(4)535 732 Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/docs/hash.3.ps b/src/util/db2/docs/hash.3.ps new file mode 100644 index 000000000..18303cfb7 --- /dev/null +++ b/src/util/db2/docs/hash.3.ps @@ -0,0 +1,292 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 136.79(HASH\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 136.79(anual HASH\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 84 S +(ME).18 E F0(hash \255 hash database access method)108 96 Q F1(SYNOPSIS)72 +112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 .29(The routine)108 165.6 R +/F3 10/Times-Italic@0 SF(dbopen)2.79 E F0 .29(is the library interf)2.79 F .29 +(ace to database \214les.)-.1 F .29 +(One of the supported \214le formats is hash \214les.)5.29 F .974 +(The general description of the database access methods is in)108 177.6 R F3 +(dbopen)3.475 E F0 .975(\(3\), this manual page describes only).24 F +(the hash speci\214c information.)108 189.6 Q(The hash data structure is an e) +108 206.4 Q(xtensible, dynamic hashing scheme.)-.15 E .83 +(The access method speci\214c data structure pro)108 223.2 R .83(vided to)-.15 +F F3(dbopen)3.33 E F0 .83(is de\214ned in the include \214le as fol-)-.4 F(lo)108 235.2 Q(ws:)-.25 E(typedef struct {) +108 259.2 Q(int bsize;)144 271.2 Q(int cachesize;)144 283.2 Q(int f)144 295.2 Q +-.1(fa)-.25 G(ctor;).1 E(u_long \(*hash\)\(const v)144 307.2 Q +(oid *, size_t\);)-.2 E(int lorder;)144 319.2 Q(int nelem;)144 331.2 Q 2.5(}H) +108 343.2 S(ASHINFO;)122.52 343.2 Q +(The elements of this structure are as follo)108 360 Q(ws:)-.25 E(bsize)108 +376.8 Q F3(Bsize)144 376.8 Q F0 1.393(de\214nes the hash table b)3.893 F(uck) +-.2 E 1.393(et size, and is, by def)-.1 F 1.394(ault, 256 bytes.)-.1 F 1.394 +(It may be preferable to)6.394 F +(increase the page size for disk-resident tables and tables with lar)144 388.8 +Q(ge data items.)-.18 E(cachesize)108 405.6 Q 3.16(As)144 417.6 S .66 +(uggested maximum size, in bytes, of the memory cache.)158.27 417.6 R .659 +(This v)5.659 F .659(alue is)-.25 F F2(only)3.159 E F0(advisory)3.159 E 3.159 +(,a)-.65 G .659(nd the)514.621 417.6 R +(access method will allocate more memory rather than f)144 429.6 Q(ail.)-.1 E +-2.1 -.25(ff a)108 446.4 T(ctor).25 E F3(Ffactor)9.7 E F0 .482 +(indicates a desired density within the hash table.)2.981 F .482 +(It is an approximation of the number of)5.482 F -.1(ke)144 458.4 S .429 +(ys allo)-.05 F .429(wed to accumulate in an)-.25 F 2.929(yo)-.15 G .429(ne b) +291.454 458.4 R(uck)-.2 E .429(et, determining when the hash table gro)-.1 F +.428(ws or shrinks.)-.25 F(The def)144 470.4 Q(ault v)-.1 E(alue is 8.)-.25 E +(hash)108 487.2 Q F3(Hash)144 487.2 Q F0 .1(is a user de\214ned hash function.) +2.6 F .1(Since no hash function performs equally well on all possible)5.1 F +.924(data, the user may \214nd that the b)144 499.2 R .923 +(uilt-in hash function does poorly on a particular data set.)-.2 F(User)5.923 E +1.408(speci\214ed hash functions must tak)144 511.2 R 3.909(et)-.1 G 1.609 -.1 +(wo a)293.431 511.2 T -.18(rg).1 G 1.409 +(uments \(a pointer to a byte string and a length\) and).18 F +(return an u_long to be used as the hash v)144 523.2 Q(alue.)-.25 E 9.62 +(lorder The)108 540 R 1.597(byte order for inte)4.097 F 1.596 +(gers in the stored database metadata.)-.15 F 1.596 +(The number should represent the)6.596 F .688(order as an inte)144 552 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E F3(lor)3.189 E(der)-.37 E F0 .689 +(is 0 \(no)3.189 F .822(order is speci\214ed\) the current host order is used.) +144 564 R .822(If the)5.822 F .822(\214le already e)5.822 F .821 +(xists, the speci\214ed v)-.15 F .821(alue is)-.25 F(ignored and the v)144 576 +Q(alue speci\214ed when the tree w)-.25 E(as created is used.)-.1 E(nelem)108 +592.8 Q F3(Nelem)144 592.8 Q F0 .701 +(is an estimate of the \214nal size of the hash table.)3.2 F .701 +(If not set or set too lo)5.701 F 2.001 -.65(w, h)-.25 H .701(ash tables will) +.65 F -.15(ex)144 604.8 S .448(pand gracefully as k).15 F -.15(ey)-.1 G 2.948 +(sa).15 G .448(re entered, although a slight performance de)255.912 604.8 R +.447(gradation may be noticed.)-.15 F(The def)144 616.8 Q(ault v)-.1 E +(alue is 1.)-.25 E .79(If the \214le already e)108 633.6 R .79 +(xists \(and the O_TR)-.15 F .79(UNC \215ag is not speci\214ed\), the v)-.4 F +.79(alues speci\214ed for the parameters)-.25 F(bsize, f)108 645.6 Q -.1(fa) +-.25 G(ctor).1 E 2.5(,l)-.4 G(order and nelem are ignored and the v)167.23 +645.6 Q(alues speci\214ed when the tree w)-.25 E(as created are used.)-.1 E +1.232(If a hash function is speci\214ed,)108 662.4 R F3(hash_open)3.731 E F0 +1.231(will attempt to determine if the hash function speci\214ed is the)3.731 F +(same as the one with which the database w)108 674.4 Q(as created, and will f) +-.1 E(ail if it is not.)-.1 E(Backw)108 691.2 Q .861(ard compatible interf)-.1 +F .861(aces to the routines described in)-.1 F F3(dbm)3.362 E F0 .862 +(\(3\), and).32 F F3(ndbm)3.362 E F0 .862(\(3\) are pro).32 F .862(vided, ho) +-.15 F(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G(4.4 Berk)72 732 Q(ele)-.1 E 2.5 +(yD)-.15 G(istrib)132.57 732 Q 96.815(ution August)-.2 F(17, 1993)2.5 E(1)535 +732 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 136.79(HASH\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 136.79(anual HASH\(3\))340.17 48 R(these interf)108 84 Q +(aces are not compatible with pre)-.1 E(vious \214le formats.)-.25 E/F1 9 +/Times-Bold@0 SF(SEE ALSO)72 100.8 Q/F2 10/Times-Italic@0 SF(btr)108 112.8 Q +(ee)-.37 E F0(\(3\),).18 E F2(dbopen)2.5 E F0(\(3\),).24 E F2(mpool)2.5 E F0 +(\(3\),).51 E F2 -.37(re)2.5 G(cno).37 E F0(\(3\)).18 E F2(Dynamic Hash T)108 +136.8 Q(ables)-.92 E F0 2.5(,P).27 G(er)206.79 136.8 Q(-Ak)-.2 E 2.5(eL)-.1 G +(arson, Communications of the A)242.86 136.8 Q(CM, April 1988.)-.4 E F2 2.5(AN) +108 160.8 S .3 -.15(ew H)123.28 160.8 T(ash P).15 E(ac)-.8 E(ka)-.2 E .2 -.1 +(ge f)-.1 H(or UNIX).1 E F0 2.5(,M).94 G(ar)248.41 160.8 Q(go Seltzer)-.18 E +2.5(,U)-.4 G(SENIX Proceedings, W)308.09 160.8 Q(inter 1991.)-.4 E F1 -.09(BU) +72 177.6 S(GS).09 E F0(Only big and little endian byte order is supported.)108 +189.6 Q(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 96.815 +(ution August)-.2 F(17, 1993)2.5 E(2)535 732 Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/docs/hash.usenix.ps b/src/util/db2/docs/hash.usenix.ps new file mode 100644 index 000000000..acdea0992 --- /dev/null +++ b/src/util/db2/docs/hash.usenix.ps @@ -0,0 +1,12209 @@ +%!PS-Adobe-1.0 +%%Creator: utopia:margo (& Seltzer,608-13E,8072,) +%%Title: stdin (ditroff) +%%CreationDate: Tue Dec 11 15:06:45 1990 +%%EndComments +% @(#)psdit.pro 1.3 4/15/88 +% lib/psdit.pro -- prolog for psdit (ditroff) files +% Copyright (c) 1984, 1985 Adobe Systems Incorporated. All Rights Reserved. +% last edit: shore Sat Nov 23 20:28:03 1985 +% RCSID: $Header$ + +% Changed by Edward Wang (edward@ucbarpa.berkeley.edu) to handle graphics, +% 17 Feb, 87. + +/$DITroff 140 dict def $DITroff begin +/fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def +/xi{0 72 11 mul translate 72 resolution div dup neg scale 0 0 moveto + /fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def F + /pagesave save def}def +/PB{save /psv exch def currentpoint translate + resolution 72 div dup neg scale 0 0 moveto}def +/PE{psv restore}def +/arctoobig 90 def /arctoosmall .05 def +/m1 matrix def /m2 matrix def /m3 matrix def /oldmat matrix def +/tan{dup sin exch cos div}def +/point{resolution 72 div mul}def +/dround {transform round exch round exch itransform}def +/xT{/devname exch def}def +/xr{/mh exch def /my exch def /resolution exch def}def +/xp{}def +/xs{docsave restore end}def +/xt{}def +/xf{/fontname exch def /slotno exch def fontnames slotno get fontname eq not + {fonts slotno fontname findfont put fontnames slotno fontname put}if}def +/xH{/fontheight exch def F}def +/xS{/fontslant exch def F}def +/s{/fontsize exch def /fontheight fontsize def F}def +/f{/fontnum exch def F}def +/F{fontheight 0 le{/fontheight fontsize def}if + fonts fontnum get fontsize point 0 0 fontheight point neg 0 0 m1 astore + fontslant 0 ne{1 0 fontslant tan 1 0 0 m2 astore m3 concatmatrix}if + makefont setfont .04 fontsize point mul 0 dround pop setlinewidth}def +/X{exch currentpoint exch pop moveto show}def +/N{3 1 roll moveto show}def +/Y{exch currentpoint pop exch moveto show}def +/S{show}def +/ditpush{}def/ditpop{}def +/AX{3 -1 roll currentpoint exch pop moveto 0 exch ashow}def +/AN{4 2 roll moveto 0 exch ashow}def +/AY{3 -1 roll currentpoint pop exch moveto 0 exch ashow}def +/AS{0 exch ashow}def +/MX{currentpoint exch pop moveto}def +/MY{currentpoint pop exch moveto}def +/MXY{moveto}def +/cb{pop}def % action on unknown char -- nothing for now +/n{}def/w{}def +/p{pop showpage pagesave restore /pagesave save def}def +/Dt{/Dlinewidth exch def}def 1 Dt +/Ds{/Ddash exch def}def -1 Ds +/Di{/Dstipple exch def}def 1 Di +/Dsetlinewidth{2 Dlinewidth mul setlinewidth}def +/Dsetdash{Ddash 4 eq{[8 12]}{Ddash 16 eq{[32 36]} + {Ddash 20 eq{[32 12 8 12]}{[]}ifelse}ifelse}ifelse 0 setdash}def +/Dstroke{gsave Dsetlinewidth Dsetdash 1 setlinecap stroke grestore + currentpoint newpath moveto}def +/Dl{rlineto Dstroke}def +/arcellipse{/diamv exch def /diamh exch def oldmat currentmatrix pop + currentpoint translate 1 diamv diamh div scale /rad diamh 2 div def + currentpoint exch rad add exch rad -180 180 arc oldmat setmatrix}def +/Dc{dup arcellipse Dstroke}def +/De{arcellipse Dstroke}def +/Da{/endv exch def /endh exch def /centerv exch def /centerh exch def + /cradius centerv centerv mul centerh centerh mul add sqrt def + /eradius endv endv mul endh endh mul add sqrt def + /endang endv endh atan def + /startang centerv neg centerh neg atan def + /sweep startang endang sub dup 0 lt{360 add}if def + sweep arctoobig gt + {/midang startang sweep 2 div sub def /midrad cradius eradius add 2 div def + /midh midang cos midrad mul def /midv midang sin midrad mul def + midh neg midv neg endh endv centerh centerv midh midv Da + Da} + {sweep arctoosmall ge + {/controldelt 1 sweep 2 div cos sub 3 sweep 2 div sin mul div 4 mul def + centerv neg controldelt mul centerh controldelt mul + endv neg controldelt mul centerh add endh add + endh controldelt mul centerv add endv add + centerh endh add centerv endv add rcurveto Dstroke} + {centerh endh add centerv endv add rlineto Dstroke} + ifelse} + ifelse}def +/Dpatterns[ +[%cf[widthbits] +[8<0000000000000010>] +[8<0411040040114000>] +[8<0204081020408001>] +[8<0000103810000000>] +[8<6699996666999966>] +[8<0000800100001008>] +[8<81c36666c3810000>] +[8<0f0e0c0800000000>] +[8<0000000000000010>] +[8<0411040040114000>] +[8<0204081020408001>] +[8<0000001038100000>] +[8<6699996666999966>] +[8<0000800100001008>] +[8<81c36666c3810000>] +[8<0f0e0c0800000000>] +[8<0042660000246600>] +[8<0000990000990000>] +[8<0804020180402010>] +[8<2418814242811824>] +[8<6699996666999966>] +[8<8000000008000000>] +[8<00001c3e363e1c00>] +[8<0000000000000000>] +[32<00000040000000c00000004000000040000000e0000000000000000000000000>] +[32<00000000000060000000900000002000000040000000f0000000000000000000>] +[32<000000000000000000e0000000100000006000000010000000e0000000000000>] +[32<00000000000000002000000060000000a0000000f00000002000000000000000>] +[32<0000000e0000000000000000000000000000000f000000080000000e00000001>] +[32<0000090000000600000000000000000000000000000007000000080000000e00>] +[32<00010000000200000004000000040000000000000000000000000000000f0000>] +[32<0900000006000000090000000600000000000000000000000000000006000000>]] +[%ug +[8<0000020000000000>] +[8<0000020000002000>] +[8<0004020000002000>] +[8<0004020000402000>] +[8<0004060000402000>] +[8<0004060000406000>] +[8<0006060000406000>] +[8<0006060000606000>] +[8<00060e0000606000>] +[8<00060e000060e000>] +[8<00070e000060e000>] +[8<00070e000070e000>] +[8<00070e020070e000>] +[8<00070e020070e020>] +[8<04070e020070e020>] +[8<04070e024070e020>] +[8<04070e064070e020>] +[8<04070e064070e060>] +[8<06070e064070e060>] +[8<06070e066070e060>] +[8<06070f066070e060>] +[8<06070f066070f060>] +[8<060f0f066070f060>] +[8<060f0f0660f0f060>] +[8<060f0f0760f0f060>] +[8<060f0f0760f0f070>] +[8<0e0f0f0760f0f070>] +[8<0e0f0f07e0f0f070>] +[8<0e0f0f0fe0f0f070>] +[8<0e0f0f0fe0f0f0f0>] +[8<0f0f0f0fe0f0f0f0>] +[8<0f0f0f0ff0f0f0f0>] +[8<1f0f0f0ff0f0f0f0>] +[8<1f0f0f0ff1f0f0f0>] +[8<1f0f0f8ff1f0f0f0>] +[8<1f0f0f8ff1f0f0f8>] +[8<9f0f0f8ff1f0f0f8>] +[8<9f0f0f8ff9f0f0f8>] +[8<9f0f0f9ff9f0f0f8>] +[8<9f0f0f9ff9f0f0f9>] +[8<9f8f0f9ff9f0f0f9>] +[8<9f8f0f9ff9f8f0f9>] +[8<9f8f1f9ff9f8f0f9>] +[8<9f8f1f9ff9f8f1f9>] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8]] +[%mg +[8<8000000000000000>] +[8<0822080080228000>] +[8<0204081020408001>] +[8<40e0400000000000>] +[8<66999966>] +[8<8001000010080000>] +[8<81c36666c3810000>] +[8] +[16<07c00f801f003e007c00f800f001e003c007800f001f003e007c00f801f003e0>] +[16<1f000f8007c003e001f000f8007c003e001f800fc007e003f001f8007c003e00>] +[8] +[16<0040008001000200040008001000200040008000000100020004000800100020>] +[16<0040002000100008000400020001800040002000100008000400020001000080>] +[16<1fc03fe07df0f8f8f07de03fc01f800fc01fe03ff07df8f87df03fe01fc00f80>] +[8<80>] +[8<8040201000000000>] +[8<84cc000048cc0000>] +[8<9900009900000000>] +[8<08040201804020100800020180002010>] +[8<2418814242811824>] +[8<66999966>] +[8<8000000008000000>] +[8<70f8d8f870000000>] +[8<0814224180402010>] +[8] +[8<018245aa45820100>] +[8<221c224180808041>] +[8<88000000>] +[8<0855800080550800>] +[8<2844004482440044>] +[8<0810204080412214>] +[8<00>]]]def +/Dfill{ + transform /maxy exch def /maxx exch def + transform /miny exch def /minx exch def + minx maxx gt{/minx maxx /maxx minx def def}if + miny maxy gt{/miny maxy /maxy miny def def}if + Dpatterns Dstipple 1 sub get exch 1 sub get + aload pop /stip exch def /stipw exch def /stiph 128 def + /imatrix[stipw 0 0 stiph 0 0]def + /tmatrix[stipw 0 0 stiph 0 0]def + /minx minx cvi stiph idiv stiph mul def + /miny miny cvi stipw idiv stipw mul def + gsave eoclip 0 setgray + miny stiph maxy{ + tmatrix exch 5 exch put + minx stipw maxx{ + tmatrix exch 4 exch put tmatrix setmatrix + stipw stiph true imatrix {stip} imagemask + }for + }for + grestore +}def +/Dp{Dfill Dstroke}def +/DP{Dfill currentpoint newpath moveto}def +end + +/ditstart{$DITroff begin + /nfonts 60 def % NFONTS makedev/ditroff dependent! + /fonts[nfonts{0}repeat]def + /fontnames[nfonts{()}repeat]def +/docsave save def +}def + +% character outcalls +/oc{ + /pswid exch def /cc exch def /name exch def + /ditwid pswid fontsize mul resolution mul 72000 div def + /ditsiz fontsize resolution mul 72 div def + ocprocs name known{ocprocs name get exec}{name cb}ifelse +}def +/fractm [.65 0 0 .6 0 0] def +/fraction{ + /fden exch def /fnum exch def gsave /cf currentfont def + cf fractm makefont setfont 0 .3 dm 2 copy neg rmoveto + fnum show rmoveto currentfont cf setfont(\244)show setfont fden show + grestore ditwid 0 rmoveto +}def +/oce{grestore ditwid 0 rmoveto}def +/dm{ditsiz mul}def +/ocprocs 50 dict def ocprocs begin +(14){(1)(4)fraction}def +(12){(1)(2)fraction}def +(34){(3)(4)fraction}def +(13){(1)(3)fraction}def +(23){(2)(3)fraction}def +(18){(1)(8)fraction}def +(38){(3)(8)fraction}def +(58){(5)(8)fraction}def +(78){(7)(8)fraction}def +(sr){gsave 0 .06 dm rmoveto(\326)show oce}def +(is){gsave 0 .15 dm rmoveto(\362)show oce}def +(->){gsave 0 .02 dm rmoveto(\256)show oce}def +(<-){gsave 0 .02 dm rmoveto(\254)show oce}def +(==){gsave 0 .05 dm rmoveto(\272)show oce}def +(uc){gsave currentpoint 400 .009 dm mul add translate + 8 -8 scale ucseal oce}def +end + +% an attempt at a PostScript FONT to implement ditroff special chars +% this will enable us to +% cache the little buggers +% generate faster, more compact PS out of psdit +% confuse everyone (including myself)! +50 dict dup begin +/FontType 3 def +/FontName /DIThacks def +/FontMatrix [.001 0 0 .001 0 0] def +/FontBBox [-260 -260 900 900] def% a lie but ... +/Encoding 256 array def +0 1 255{Encoding exch /.notdef put}for +Encoding + dup 8#040/space put %space + dup 8#110/rc put %right ceil + dup 8#111/lt put %left top curl + dup 8#112/bv put %bold vert + dup 8#113/lk put %left mid curl + dup 8#114/lb put %left bot curl + dup 8#115/rt put %right top curl + dup 8#116/rk put %right mid curl + dup 8#117/rb put %right bot curl + dup 8#120/rf put %right floor + dup 8#121/lf put %left floor + dup 8#122/lc put %left ceil + dup 8#140/sq put %square + dup 8#141/bx put %box + dup 8#142/ci put %circle + dup 8#143/br put %box rule + dup 8#144/rn put %root extender + dup 8#145/vr put %vertical rule + dup 8#146/ob put %outline bullet + dup 8#147/bu put %bullet + dup 8#150/ru put %rule + dup 8#151/ul put %underline + pop +/DITfd 100 dict def +/BuildChar{0 begin + /cc exch def /fd exch def + /charname fd /Encoding get cc get def + /charwid fd /Metrics get charname get def + /charproc fd /CharProcs get charname get def + charwid 0 fd /FontBBox get aload pop setcachedevice + 2 setlinejoin 40 setlinewidth + newpath 0 0 moveto gsave charproc grestore + end}def +/BuildChar load 0 DITfd put +/CharProcs 50 dict def +CharProcs begin +/space{}def +/.notdef{}def +/ru{500 0 rls}def +/rn{0 840 moveto 500 0 rls}def +/vr{0 800 moveto 0 -770 rls}def +/bv{0 800 moveto 0 -1000 rls}def +/br{0 840 moveto 0 -1000 rls}def +/ul{0 -140 moveto 500 0 rls}def +/ob{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath stroke}def +/bu{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath fill}def +/sq{80 0 rmoveto currentpoint dround newpath moveto + 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath stroke}def +/bx{80 0 rmoveto currentpoint dround newpath moveto + 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath fill}def +/ci{500 360 rmoveto currentpoint newpath 333 0 360 arc + 50 setlinewidth stroke}def + +/lt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 add exch s4 a4p stroke}def +/lb{0 800 moveto 0 -550 rlineto currx -200 2cx s4 add exch s4 a4p stroke}def +/rt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 sub exch s4 a4p stroke}def +/rb{0 800 moveto 0 -500 rlineto currx -200 2cx s4 sub exch s4 a4p stroke}def +/lk{0 800 moveto 0 300 -300 300 s4 arcto pop pop 1000 sub + 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def +/rk{0 800 moveto 0 300 s2 300 s4 arcto pop pop 1000 sub + 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def +/lf{0 800 moveto 0 -1000 rlineto s4 0 rls}def +/rf{0 800 moveto 0 -1000 rlineto s4 neg 0 rls}def +/lc{0 -200 moveto 0 1000 rlineto s4 0 rls}def +/rc{0 -200 moveto 0 1000 rlineto s4 neg 0 rls}def +end + +/Metrics 50 dict def Metrics begin +/.notdef 0 def +/space 500 def +/ru 500 def +/br 0 def +/lt 416 def +/lb 416 def +/rt 416 def +/rb 416 def +/lk 416 def +/rk 416 def +/rc 416 def +/lc 416 def +/rf 416 def +/lf 416 def +/bv 416 def +/ob 350 def +/bu 350 def +/ci 750 def +/bx 750 def +/sq 750 def +/rn 500 def +/ul 500 def +/vr 0 def +end + +DITfd begin +/s2 500 def /s4 250 def /s3 333 def +/a4p{arcto pop pop pop pop}def +/2cx{2 copy exch}def +/rls{rlineto stroke}def +/currx{currentpoint pop}def +/dround{transform round exch round exch itransform} def +end +end +/DIThacks exch definefont pop +ditstart +(psc)xT +576 1 1 xr +1(Times-Roman)xf 1 f +2(Times-Italic)xf 2 f +3(Times-Bold)xf 3 f +4(Times-BoldItalic)xf 4 f +5(Helvetica)xf 5 f +6(Helvetica-Bold)xf 6 f +7(Courier)xf 7 f +8(Courier-Bold)xf 8 f +9(Symbol)xf 9 f +10(DIThacks)xf 10 f +10 s +1 f +xi +%%EndProlog + +%%Page: 1 1 +10 s 10 xH 0 xS 1 f +3 f +22 s +1249 626(A)N +1420(N)X +1547(ew)X +1796(H)X +1933(ashing)X +2467(P)X +2574(ackage)X +3136(for)X +3405(U)X +3532(N)X +3659(IX)X +2 f +20 s +3855 562(1)N +1 f +12 s +1607 779(Margo)N +1887(Seltzer)X +9 f +2179(-)X +1 f +2256(University)X +2686(of)X +2790(California,)X +3229(Berkeley)X +2015 875(Ozan)N +2242(Yigit)X +9 f +2464(-)X +1 f +2541(York)X +2762(University)X +3 f +2331 1086(ABSTRACT)N +1 f +10 s +1152 1222(UNIX)N +1385(support)X +1657(of)X +1756(disk)X +1921(oriented)X +2216(hashing)X +2497(was)X +2654(originally)X +2997(provided)X +3314(by)X +2 f +3426(dbm)X +1 f +3595([ATT79])X +3916(and)X +1152 1310(subsequently)N +1595(improved)X +1927(upon)X +2112(in)X +2 f +2199(ndbm)X +1 f +2402([BSD86].)X +2735(In)X +2826(AT&T)X +3068(System)X +3327(V,)X +3429(in-memory)X +3809(hashed)X +1152 1398(storage)N +1420(and)X +1572(access)X +1814(support)X +2090(was)X +2251(added)X +2479(in)X +2577(the)X +2 f +2711(hsearch)X +1 f +3000(library)X +3249(routines)X +3542([ATT85].)X +3907(The)X +1152 1486(result)N +1367(is)X +1457(a)X +1530(system)X +1789(with)X +1968(two)X +2125(incompatible)X +2580(hashing)X +2865(schemes,)X +3193(each)X +3377(with)X +3555(its)X +3666(own)X +3840(set)X +3965(of)X +1152 1574(shortcomings.)N +1152 1688(This)N +1316(paper)X +1517(presents)X +1802(the)X +1922(design)X +2152(and)X +2289(performance)X +2717(characteristics)X +3198(of)X +3286(a)X +3343(new)X +3498(hashing)X +3768(package)X +1152 1776(providing)N +1483(a)X +1539(superset)X +1822(of)X +1909(the)X +2027(functionality)X +2456(provided)X +2761(by)X +2 f +2861(dbm)X +1 f +3019(and)X +2 f +3155(hsearch)X +1 f +3409(.)X +3469(The)X +3614(new)X +3768(package)X +1152 1864(uses)N +1322(linear)X +1537(hashing)X +1818(to)X +1912(provide)X +2189(ef\256cient)X +2484(support)X +2755(of)X +2853(both)X +3026(memory)X +3324(based)X +3538(and)X +3685(disk)X +3849(based)X +1152 1952(hash)N +1319(tables)X +1526(with)X +1688(performance)X +2115(superior)X +2398(to)X +2480(both)X +2 f +2642(dbm)X +1 f +2800(and)X +2 f +2936(hsearch)X +1 f +3210(under)X +3413(most)X +3588(conditions.)X +3 f +1380 2128(Introduction)N +1 f +892 2260(Current)N +1196(UNIX)X +1456(systems)X +1768(offer)X +1984(two)X +2163(forms)X +2409(of)X +720 2348(hashed)N +973(data)X +1137(access.)X +2 f +1413(Dbm)X +1 f +1599(and)X +1745(its)X +1850(derivatives)X +2231(provide)X +720 2436(keyed)N +939(access)X +1171(to)X +1259(disk)X +1418(resident)X +1698(data)X +1858(while)X +2 f +2062(hsearch)X +1 f +2342(pro-)X +720 2524(vides)N +929(access)X +1175(for)X +1309(memory)X +1616(resident)X +1910(data.)X +2124(These)X +2356(two)X +720 2612(access)N +979(methods)X +1302(are)X +1453(incompatible)X +1923(in)X +2037(that)X +2209(memory)X +720 2700(resident)N +1011(hash)X +1195(tables)X +1419(may)X +1593(not)X +1731(be)X +1843(stored)X +2075(on)X +2191(disk)X +2360(and)X +720 2788(disk)N +884(resident)X +1169(tables)X +1387(cannot)X +1632(be)X +1739(read)X +1909(into)X +2063(memory)X +2360(and)X +720 2876(accessed)N +1022(using)X +1215(the)X +1333(in-memory)X +1709(routines.)X +2 f +892 2990(Dbm)N +1 f +1091(has)X +1241(several)X +1512(shortcomings.)X +2026(Since)X +2247(data)X +2423(is)X +720 3078(assumed)N +1032(to)X +1130(be)X +1242(disk)X +1411(resident,)X +1721(each)X +1905(access)X +2146(requires)X +2440(a)X +720 3166(system)N +963(call,)X +1120(and)X +1257(almost)X +1491(certainly,)X +1813(a)X +1869(disk)X +2022(operation.)X +2365(For)X +720 3254(extremely)N +1072(large)X +1264(databases,)X +1623(where)X +1851(caching)X +2131(is)X +2214(unlikely)X +720 3342(to)N +810(be)X +914(effective,)X +1244(this)X +1386(is)X +1466(acceptable,)X +1853(however,)X +2177(when)X +2378(the)X +720 3430(database)N +1022(is)X +1100(small)X +1298(\(i.e.)X +1447(the)X +1569(password)X +1896(\256le\),)X +2069(performance)X +720 3518(improvements)N +1204(can)X +1342(be)X +1443(obtained)X +1744(through)X +2018(caching)X +2293(pages)X +720 3606(of)N +818(the)X +947(database)X +1255(in)X +1348(memory.)X +1685(In)X +1782(addition,)X +2 f +2094(dbm)X +1 f +2262(cannot)X +720 3694(store)N +902(data)X +1062(items)X +1261(whose)X +1492(total)X +1660(key)X +1802(and)X +1943(data)X +2102(size)X +2252(exceed)X +720 3782(the)N +850(page)X +1034(size)X +1191(of)X +1290(the)X +1420(hash)X +1599(table.)X +1827(Similarly,)X +2176(if)X +2257(two)X +2409(or)X +720 3870(more)N +907(keys)X +1076(produce)X +1357(the)X +1477(same)X +1664(hash)X +1833(value)X +2029(and)X +2166(their)X +2334(total)X +720 3958(size)N +876(exceeds)X +1162(the)X +1291(page)X +1474(size,)X +1650(the)X +1779(table)X +1966(cannot)X +2210(store)X +2396(all)X +720 4046(the)N +838(colliding)X +1142(keys.)X +892 4160(The)N +1050(in-memory)X +2 f +1439(hsearch)X +1 f +1725(routines)X +2015(have)X +2199(different)X +720 4248(shortcomings.)N +1219(First,)X +1413(the)X +1539(notion)X +1771(of)X +1865(a)X +1928(single)X +2146(hash)X +2320(table)X +720 4336(is)N +807(embedded)X +1171(in)X +1266(the)X +1397(interface,)X +1732(preventing)X +2108(an)X +2217(applica-)X +720 4424(tion)N +902(from)X +1116(accessing)X +1482(multiple)X +1806(tables)X +2050(concurrently.)X +720 4512(Secondly,)N +1063(the)X +1186(routine)X +1438(to)X +1525(create)X +1743(a)X +1804(hash)X +1976(table)X +2157(requires)X +2440(a)X +720 4600(parameter)N +1066(which)X +1286(declares)X +1573(the)X +1694(size)X +1842(of)X +1932(the)X +2053(hash)X +2223(table.)X +2422(If)X +720 4688(this)N +856(size)X +1001(is)X +1074(set)X +1183(too)X +1305(low,)X +1465(performance)X +1892(degradation)X +2291(or)X +2378(the)X +720 4776(inability)N +1008(to)X +1092(add)X +1230(items)X +1425(to)X +1509(the)X +1628(table)X +1805(may)X +1964(result.)X +2223(In)X +2311(addi-)X +720 4864(tion,)N +2 f +910(hsearch)X +1 f +1210(requires)X +1515(that)X +1681(the)X +1825(application)X +2226(allocate)X +720 4952(memory)N +1037(for)X +1181(the)X +1329(key)X +1495(and)X +1661(data)X +1845(items.)X +2108(Lastly,)X +2378(the)X +2 f +720 5040(hsearch)N +1 f +1013(routines)X +1310(provide)X +1594(no)X +1713(interface)X +2034(to)X +2135(store)X +2329(hash)X +720 5128(tables)N +927(on)X +1027(disk.)X +16 s +720 5593 MXY +864 0 Dl +2 f +8 s +760 5648(1)N +1 f +9 s +5673(UNIX)Y +990(is)X +1056(a)X +1106(registered)X +1408(trademark)X +1718(of)X +1796(AT&T.)X +10 s +2878 2128(The)N +3032(goal)X +3199(of)X +3295(our)X +3431(work)X +3625(was)X +3779(to)X +3870(design)X +4108(and)X +4253(imple-)X +2706 2216(ment)N +2900(a)X +2970(new)X +3138(package)X +3436(that)X +3590(provides)X +3899(a)X +3968(superset)X +4264(of)X +4364(the)X +2706 2304(functionality)N +3144(of)X +3240(both)X +2 f +3411(dbm)X +1 f +3578(and)X +2 f +3723(hsearch)X +1 f +3977(.)X +4045(The)X +4198(package)X +2706 2392(had)N +2871(to)X +2982(overcome)X +3348(the)X +3495(interface)X +3826(shortcomings)X +4306(cited)X +2706 2480(above)N +2930(and)X +3078(its)X +3185(implementation)X +3719(had)X +3867(to)X +3961(provide)X +4238(perfor-)X +2706 2568(mance)N +2942(equal)X +3142(or)X +3235(superior)X +3524(to)X +3612(that)X +3758(of)X +3851(the)X +3975(existing)X +4253(imple-)X +2706 2656(mentations.)N +3152(In)X +3274(order)X +3498(to)X +3614(provide)X +3913(a)X +4003(compact)X +4329(disk)X +2706 2744(representation,)N +3224(graceful)X +3531(table)X +3729(growth,)X +4018(and)X +4176(expected)X +2706 2832(constant)N +3033(time)X +3234(performance,)X +3720(we)X +3873(selected)X +4191(Litwin's)X +2706 2920(linear)N +2923(hashing)X +3206(algorithm)X +3551([LAR88,)X +3872(LIT80].)X +4178(We)X +4324(then)X +2706 3008(enhanced)N +3037(the)X +3161(algorithm)X +3498(to)X +3586(handle)X +3826(page)X +4004(over\257ows)X +4346(and)X +2706 3096(large)N +2900(key)X +3049(handling)X +3362(with)X +3537(a)X +3606(single)X +3830(mechanism,)X +4248(named)X +2706 3184(buddy-in-waiting.)N +3 f +2975 3338(Existing)N +3274(UNIX)X +3499(Hashing)X +3802(Techniques)X +1 f +2878 3470(Over)N +3076(the)X +3210(last)X +3357(decade,)X +3637(several)X +3901(dynamic)X +4213(hashing)X +2706 3558(schemes)N +3000(have)X +3174(been)X +3348(developed)X +3700(for)X +3816(the)X +3936(UNIX)X +4159(timeshar-)X +2706 3646(ing)N +2856(system,)X +3146(starting)X +3433(with)X +3622(the)X +3767(inclusion)X +4107(of)X +2 f +4221(dbm)X +1 f +4359(,)X +4426(a)X +2706 3734(minimal)N +3008(database)X +3321(library)X +3571(written)X +3834(by)X +3950(Ken)X +4120(Thompson)X +2706 3822([THOM90],)N +3141(in)X +3248(the)X +3391(Seventh)X +3694(Edition)X +3974(UNIX)X +4220(system.)X +2706 3910(Since)N +2916(then,)X +3106(an)X +3214(extended)X +3536(version)X +3804(of)X +3903(the)X +4032(same)X +4228(library,)X +2 f +2706 3998(ndbm)N +1 f +2884(,)X +2933(and)X +3078(a)X +3142(public-domain)X +3637(clone)X +3839(of)X +3934(the)X +4060(latter,)X +2 f +4273(sdbm)X +1 f +4442(,)X +2706 4086(have)N +2902(been)X +3098(developed.)X +3491(Another)X +3797 0.1645(interface-compatible)AX +2706 4174(library)N +2 f +2950(gdbm)X +1 f +3128(,)X +3178(was)X +3333(recently)X +3622(made)X +3826(available)X +4145(as)X +4241(part)X +4395(of)X +2706 4262(the)N +2829(Free)X +2997(Software)X +3312(Foundation's)X +3759(\(FSF\))X +3970(software)X +4271(distri-)X +2706 4350(bution.)N +2878 4464(All)N +3017(of)X +3121(these)X +3323(implementations)X +3893(are)X +4029(based)X +4248(on)X +4364(the)X +2706 4552(idea)N +2871(of)X +2969(revealing)X +3299(just)X +3445(enough)X +3711(bits)X +3856(of)X +3953(a)X +4019(hash)X +4196(value)X +4400(to)X +2706 4640(locate)N +2920(a)X +2978(page)X +3151(in)X +3234(a)X +3291(single)X +3503(access.)X +3770(While)X +2 f +3987(dbm/ndbm)X +1 f +4346(and)X +2 f +2706 4728(sdbm)N +1 f +2908(map)X +3079(the)X +3210(hash)X +3390(value)X +3597(directly)X +3874(to)X +3968(a)X +4036(disk)X +4201(address,)X +2 f +2706 4816(gdbm)N +1 f +2921(uses)X +3096(the)X +3231(hash)X +3414(value)X +3624(to)X +3722(index)X +3936(into)X +4096(a)X +2 f +4168(directory)X +1 f +2706 4904([ENB88])N +3020(containing)X +3378(disk)X +3531(addresses.)X +2878 5018(The)N +2 f +3033(hsearch)X +1 f +3317(routines)X +3605(in)X +3697(System)X +3962(V)X +4049(are)X +4177(designed)X +2706 5106(to)N +2804(provide)X +3085(memory-resident)X +3669(hash)X +3852(tables.)X +4115(Since)X +4328(data)X +2706 5194(access)N +2948(does)X +3131(not)X +3269(require)X +3533(disk)X +3702(access,)X +3964(simple)X +4213(hashing)X +2706 5282(schemes)N +3010(which)X +3238(may)X +3408(require)X +3667(multiple)X +3964(probes)X +4209(into)X +4364(the)X +2706 5370(table)N +2889(are)X +3015(used.)X +3209(A)X +3294(more)X +3486(interesting)X +3851(version)X +4114(of)X +2 f +4208(hsearch)X +1 f +2706 5458(is)N +2784(a)X +2845(public)X +3070(domain)X +3335(library,)X +2 f +3594(dynahash)X +1 f +3901(,)X +3945(that)X +4089(implements)X +2706 5546(Larson's)N +3036(in-memory)X +3440(adaptation)X +3822([LAR88])X +4164(of)X +4279(linear)X +2706 5634(hashing)N +2975([LIT80].)X +3 f +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +1 f +4424(1)X + +2 p +%%Page: 2 2 +10 s 10 xH 0 xS 1 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +2 f +1074 538(dbm)N +1 f +1232(and)X +2 f +1368(ndbm)X +1 f +604 670(The)N +2 f +760(dbm)X +1 f +928(and)X +2 f +1074(ndbm)X +1 f +1282(library)X +1526(implementations)X +2089(are)X +432 758(based)N +667(on)X +799(the)X +949(same)X +1166(algorithm)X +1529(by)X +1661(Ken)X +1846(Thompson)X +432 846([THOM90,)N +824(TOR88,)X +1113(WAL84],)X +1452(but)X +1582(differ)X +1789(in)X +1879(their)X +2054(pro-)X +432 934(grammatic)N +801(interfaces.)X +1160(The)X +1311(latter)X +1502(is)X +1581(a)X +1643(modi\256ed)X +1952(version)X +432 1022(of)N +533(the)X +665(former)X +918(which)X +1148(adds)X +1328(support)X +1601(for)X +1728(multiple)X +2027(data-)X +432 1110(bases)N +634(to)X +724(be)X +828(open)X +1011(concurrently.)X +1484(The)X +1636(discussion)X +1996(of)X +2090(the)X +432 1198(algorithm)N +774(that)X +925(follows)X +1196(is)X +1280(applicable)X +1640(to)X +1732(both)X +2 f +1904(dbm)X +1 f +2072(and)X +2 f +432 1286(ndbm)N +1 f +610(.)X +604 1400(The)N +760(basic)X +956(structure)X +1268(of)X +2 f +1366(dbm)X +1 f +1535(calls)X +1712(for)X +1836(\256xed-sized)X +432 1488(disk)N +612(blocks)X +868(\(buckets\))X +1214(and)X +1377(an)X +2 f +1499(access)X +1 f +1755(function)X +2068(that)X +432 1576(maps)N +623(a)X +681(key)X +819(to)X +902(a)X +959(bucket.)X +1234(The)X +1380(interface)X +1683(routines)X +1962(use)X +2090(the)X +2 f +432 1664(access)N +1 f +673(function)X +970(to)X +1062(obtain)X +1292(the)X +1420(appropriate)X +1816(bucket)X +2060(in)X +2152(a)X +432 1752(single)N +643(disk)X +796(access.)X +604 1866(Within)N +869(the)X +2 f +1010(access)X +1 f +1263(function,)X +1593(a)X +1672(bit-randomizing)X +432 1954(hash)N +610(function)X +2 f +8 s +877 1929(2)N +1 f +10 s +940 1954(is)N +1024(used)X +1202(to)X +1294(convert)X +1565(a)X +1631(key)X +1777(into)X +1931(a)X +1997(32-bit)X +432 2042(hash)N +605(value.)X +825(Out)X +971(of)X +1064(these)X +1254(32)X +1359(bits,)X +1519(only)X +1686(as)X +1778(many)X +1981(bits)X +2121(as)X +432 2130(necessary)N +773(are)X +900(used)X +1075(to)X +1165(determine)X +1514(the)X +1639(particular)X +1974(bucket)X +432 2218(on)N +533(which)X +750(a)X +807(key)X +944(resides.)X +1228(An)X +1347(in-memory)X +1724(bitmap)X +1967(is)X +2041(used)X +432 2306(to)N +533(determine)X +893(how)X +1070(many)X +1287(bits)X +1441(are)X +1579(required.)X +1905(Each)X +2104(bit)X +432 2394(indicates)N +746(whether)X +1033(its)X +1136(associated)X +1494(bucket)X +1736(has)X +1871(been)X +2051(split)X +432 2482(yet)N +562(\(a)X +657(0)X +728(indicating)X +1079(that)X +1230(the)X +1359(bucket)X +1604(has)X +1742(not)X +1875(yet)X +2004(split\).)X +432 2570(The)N +590(use)X +730(of)X +830(the)X +961(hash)X +1141(function)X +1441(and)X +1590(the)X +1720(bitmap)X +1974(is)X +2059(best)X +432 2658(described)N +769(by)X +878(stepping)X +1177(through)X +1454(database)X +1759(creation)X +2046(with)X +432 2746(multiple)N +718(invocations)X +1107(of)X +1194(a)X +2 f +1250(store)X +1 f +1430(operation.)X +604 2860(Initially,)N +906(the)X +1033(hash)X +1209(table)X +1394(contains)X +1690(a)X +1755(single)X +1974(bucket)X +432 2948(\(bucket)N +711(0\),)X +836(the)X +972(bit)X +1094(map)X +1270(contains)X +1575(a)X +1649(single)X +1878(bit)X +2000(\(bit)X +2148(0)X +432 3036(corresponding)N +913(to)X +997(bucket)X +1233(0\),)X +1342(and)X +1480(0)X +1542(bits)X +1699(of)X +1788(a)X +1846(hash)X +2014(value)X +432 3124(are)N +560(examined)X +901(to)X +992(determine)X +1342(where)X +1568(a)X +1633(key)X +1778(is)X +1860(placed)X +2099(\(in)X +432 3212(bucket)N +670(0\).)X +801(When)X +1017(bucket)X +1255(0)X +1319(is)X +1396(full,)X +1551(its)X +1650(bit)X +1758(in)X +1844(the)X +1966(bitmap)X +432 3300(\(bit)N +564(0\))X +652(is)X +726(set,)X +856(and)X +993(its)X +1089(contents)X +1377(are)X +1497(split)X +1655(between)X +1943(buckets)X +432 3388(0)N +499(and)X +641(1,)X +727(by)X +833(considering)X +1233(the)X +1357(0)X +2 f +7 s +3356(th)Y +10 s +1 f +1480 3388(bit)N +1590(\(the)X +1741(lowest)X +1976(bit)X +2086(not)X +432 3476(previously)N +800(examined\))X +1169(of)X +1266(the)X +1393(hash)X +1569(value)X +1772(for)X +1895(each)X +2072(key)X +432 3564(within)N +668(the)X +798(bucket.)X +1064(Given)X +1292(a)X +1359(well-designed)X +1840(hash)X +2018(func-)X +432 3652(tion,)N +613(approximately)X +1112(half)X +1273(of)X +1376(the)X +1510(keys)X +1693(will)X +1853(have)X +2041(hash)X +432 3740(values)N +666(with)X +837(the)X +964(0)X +2 f +7 s +3708(th)Y +10 s +1 f +1090 3740(bit)N +1203(set.)X +1341(All)X +1471(such)X +1646(keys)X +1821(and)X +1965(associ-)X +432 3828(ated)N +586(data)X +740(are)X +859(moved)X +1097(to)X +1179(bucket)X +1413(1,)X +1493(and)X +1629(the)X +1747(rest)X +1883(remain)X +2126(in)X +432 3916(bucket)N +666(0.)X +604 4030(After)N +804(this)X +949(split,)X +1135(the)X +1262(\256le)X +1393(now)X +1560(contains)X +1856(two)X +2005(buck-)X +432 4118(ets,)N +562(and)X +699(the)X +818(bitmap)X +1061(contains)X +1349(three)X +1530(bits:)X +1687(the)X +1805(0)X +2 f +7 s +4086(th)Y +10 s +1 f +1922 4118(bit)N +2026(is)X +2099(set)X +432 4206(to)N +525(indicate)X +810(a)X +876(bucket)X +1120(0)X +1190(split)X +1357(when)X +1561(no)X +1671(bits)X +1816(of)X +1913(the)X +2041(hash)X +432 4294(value)N +648(are)X +789(considered,)X +1199(and)X +1357(two)X +1519(more)X +1726(unset)X +1937(bits)X +2094(for)X +432 4382(buckets)N +706(0)X +775(and)X +920(1.)X +1029(The)X +1183(placement)X +1542(of)X +1638(an)X +1742(incoming)X +2072(key)X +432 4470(now)N +604(requires)X +897(examination)X +1327(of)X +1428(the)X +1560(0)X +2 f +7 s +4438(th)Y +10 s +1 f +1691 4470(bit)N +1809(of)X +1910(the)X +2041(hash)X +432 4558(value,)N +667(and)X +824(the)X +963(key)X +1119(is)X +1212(placed)X +1462(either)X +1685(in)X +1787(bucket)X +2041(0)X +2121(or)X +432 4646(bucket)N +674(1.)X +782(If)X +864(either)X +1075(bucket)X +1317(0)X +1385(or)X +1480(bucket)X +1722(1)X +1790(\256lls)X +1937(up,)X +2064(it)X +2135(is)X +432 4734(split)N +598(as)X +693(before,)X +947(its)X +1050(bit)X +1162(is)X +1243(set)X +1360(in)X +1450(the)X +1576(bitmap,)X +1846(and)X +1990(a)X +2054(new)X +432 4822(set)N +541(of)X +628(unset)X +817(bits)X +952(are)X +1071(added)X +1283(to)X +1365(the)X +1483(bitmap.)X +604 4936(Each)N +791(time)X +959(we)X +1079(consider)X +1376(a)X +1437(new)X +1596(bit)X +1705(\(bit)X +1841(n\),)X +1953(we)X +2072(add)X +432 5024(2)N +2 f +7 s +4992(n)Y +9 f +509(+)X +1 f +540(1)X +10 s +595 5024(bits)N +737(to)X +826(the)X +951(bitmap)X +1199(and)X +1341(obtain)X +1567(2)X +2 f +7 s +4992(n)Y +9 f +1644(+)X +1 f +1675(1)X +10 s +1729 5024(more)N +1920(address-)X +432 5112(able)N +595(buckets)X +869(in)X +960(the)X +1087(\256le.)X +1258(As)X +1376(a)X +1441(result,)X +1668(the)X +1795(bitmap)X +2045(con-)X +432 5200(tains)N +618(the)X +751(previous)X +1062(2)X +2 f +7 s +5168(n)Y +9 f +1139(+)X +1 f +1170(1)X +2 f +10 s +9 f +5200(-)Y +1 f +1242(1)X +1317(bits)X +1467(\(1)X +2 f +9 f +1534(+)X +1 f +1578(2)X +2 f +9 f +(+)S +1 f +1662(4)X +2 f +9 f +(+)S +1 f +1746(...)X +2 f +9 f +(+)S +1 f +1850(2)X +2 f +7 s +5168(n)Y +10 s +1 f +1931 5200(\))N +1992(which)X +432 5288(trace)N +649(the)X +807(entire)X +2 f +1050(split)X +1247(history)X +1 f +1529(of)X +1656(the)X +1813(addressable)X +16 s +432 5433 MXY +864 0 Dl +2 f +8 s +472 5488(2)N +1 f +9 s +523 5513(This)N +670(bit-randomizing)X +1153(property)X +1416(is)X +1482(important)X +1780(to)X +1854(obtain)X +2052(radi-)X +432 5593(cally)N +599(different)X +874(hash)X +1033(values)X +1244(for)X +1355(nearly)X +1562(identical)X +1836(keys,)X +2012(which)X +432 5673(in)N +506(turn)X +640(avoids)X +846(clustering)X +1148(of)X +1226(such)X +1376(keys)X +1526(in)X +1600(a)X +1650(single)X +1840(bucket.)X +10 s +2418 538(buckets.)N +2590 652(Given)N +2809(a)X +2868(key)X +3007(and)X +3146(the)X +3267(bitmap)X +3512(created)X +3768(by)X +3871(this)X +4009(algo-)X +2418 740(rithm,)N +2638(we)X +2759(\256rst)X +2910(examine)X +3209(bit)X +3320(0)X +3386(of)X +3479(the)X +3603(bitmap)X +3851(\(the)X +4002(bit)X +4112(to)X +2418 828(consult)N +2673(when)X +2871(0)X +2934(bits)X +3072(of)X +3162(the)X +3283(hash)X +3453(value)X +3650(are)X +3772(being)X +3973(exam-)X +2418 916(ined\).)N +2631(If)X +2713(it)X +2785(is)X +2866(set)X +2982(\(indicating)X +3356(that)X +3503(the)X +3628(bucket)X +3869(split\),)X +4080(we)X +2418 1004(begin)N +2617(considering)X +3012(the)X +3131(bits)X +3267(of)X +3355(the)X +3473(32-bit)X +3684(hash)X +3851(value.)X +4085(As)X +2418 1092(bit)N +2525(n)X +2587(is)X +2662(revealed,)X +2977(a)X +3035(mask)X +3226(equal)X +3422(to)X +3506(2)X +2 f +7 s +1060(n)Y +9 f +3583(+)X +1 f +3614(1)X +2 f +10 s +9 f +1092(-)Y +1 f +3686(1)X +3748(will)X +3894(yield)X +4076(the)X +2418 1180(current)N +2675(bucket)X +2918(address.)X +3228(Adding)X +3496(2)X +2 f +7 s +1148(n)Y +9 f +3573(+)X +1 f +3604(1)X +2 f +10 s +9 f +1180(-)Y +1 f +3676(1)X +3744(to)X +3834(the)X +3960(bucket)X +2418 1268(address)N +2701(identi\256es)X +3035(which)X +3272(bit)X +3397(in)X +3500(the)X +3639(bitmap)X +3902(must)X +4098(be)X +2418 1356(checked.)N +2743(We)X +2876(continue)X +3173(revealing)X +3493(bits)X +3628(of)X +3715(the)X +3833(hash)X +4000(value)X +2418 1444(until)N +2591(all)X +2698(set)X +2814(bits)X +2955(in)X +3043(the)X +3167(bitmap)X +3415(are)X +3540(exhausted.)X +3907(The)X +4058(fol-)X +2418 1532(lowing)N +2682(algorithm,)X +3055(a)X +3133(simpli\256cation)X +3614(of)X +3723(the)X +3863(algorithm)X +2418 1620(due)N +2565(to)X +2658(Ken)X +2823(Thompson)X +3196([THOM90,)X +3590(TOR88],)X +3908(uses)X +4076(the)X +2418 1708(hash)N +2625(value)X +2839(and)X +2995(the)X +3133(bitmap)X +3395(to)X +3497(calculate)X +3823(the)X +3960(bucket)X +2418 1796(address)N +2679(as)X +2766(discussed)X +3093(above.)X +0(Courier)xf 0 f +1 f +0 f +8 s +2418 2095(hash)N +2608(=)X +2684 -0.4038(calchash\(key\);)AX +2418 2183(mask)N +2608(=)X +2684(0;)X +2418 2271(while)N +2646 -0.4018(\(isbitset\(\(hash)AX +3254(&)X +3330(mask\))X +3558(+)X +3634(mask\)\))X +2706 2359(mask)N +2896(=)X +2972(\(mask)X +3200(<<)X +3314(1\))X +3428(+)X +3504(1;)X +2418 2447(bucket)N +2684(=)X +2760(hash)X +2950(&)X +3026(mask;)X +2 f +10 s +3211 2812(sdbm)N +1 f +2590 2944(The)N +2 f +2738(sdbm)X +1 f +2930(library)X +3167(is)X +3243(a)X +3302(public-domain)X +3791(clone)X +3987(of)X +4076(the)X +2 f +2418 3032(ndbm)N +1 f +2638(library,)X +2914(developed)X +3286(by)X +3408(Ozan)X +3620(Yigit)X +3826(to)X +3929(provide)X +2 f +2418 3120(ndbm)N +1 f +2596('s)X +2692(functionality)X +3139(under)X +3359(some)X +3565(versions)X +3869(of)X +3973(UNIX)X +2418 3208(that)N +2559(exclude)X +2830(it)X +2894(for)X +3008(licensing)X +3317(reasons)X +3578([YIG89].)X +3895(The)X +4040(pro-)X +2418 3296(grammer)N +2735(interface,)X +3064(and)X +3207(the)X +3332(basic)X +3524(structure)X +3832(of)X +2 f +3926(sdbm)X +1 f +4121(is)X +2418 3384(identical)N +2733(to)X +2 f +2834(ndbm)X +1 f +3051(but)X +3192(internal)X +3476(details)X +3723(of)X +3828(the)X +2 f +3964(access)X +1 f +2418 3472(function,)N +2726(such)X +2894(as)X +2982(the)X +3101(calculation)X +3474(of)X +3561(the)X +3679(bucket)X +3913(address,)X +2418 3560(and)N +2563(the)X +2690(use)X +2825(of)X +2920(different)X +3225(hash)X +3400(functions)X +3726(make)X +3928(the)X +4054(two)X +2418 3648(incompatible)N +2856(at)X +2934(the)X +3052(database)X +3349(level.)X +2590 3762(The)N +2 f +2740(sdbm)X +1 f +2934(library)X +3173(is)X +3251(based)X +3458(on)X +3562(a)X +3622(simpli\256ed)X +3965(imple-)X +2418 3850(mentation)N +2778(of)X +2885(Larson's)X +3206(1978)X +2 f +3406(dynamic)X +3717(hashing)X +1 f +4009(algo-)X +2418 3938(rithm)N +2616(including)X +2943(the)X +2 f +3066(re\256nements)X +3461(and)X +3605(variations)X +1 f +3953(of)X +4044(sec-)X +2418 4026(tion)N +2562(5)X +2622([LAR78].)X +2956(Larson's)X +3257(original)X +3526(algorithm)X +3857(calls)X +4024(for)X +4138(a)X +2418 4114(forest)N +2635(of)X +2736(binary)X +2975(hash)X +3156(trees)X +3341(that)X +3494(are)X +3626(accessed)X +3941(by)X +4054(two)X +2418 4202(hash)N +2586(functions.)X +2925(The)X +3071(\256rst)X +3216(hash)X +3384(function)X +3672(selects)X +3907(a)X +3964(partic-)X +2418 4290(ular)N +2571(tree)X +2720(within)X +2952(the)X +3078(forest.)X +3309(The)X +3462(second)X +3713(hash)X +3887(function,)X +2418 4378(which)N +2659(is)X +2757(required)X +3070(to)X +3177(be)X +3297(a)X +3377(boolean)X +3675(pseudo-random)X +2418 4466(number)N +2687(generator)X +3015(that)X +3159(is)X +3236(seeded)X +3479(by)X +3583(the)X +3705(key,)X +3865(is)X +3942(used)X +4112(to)X +2418 4554(traverse)N +2733(the)X +2890(tree)X +3070(until)X +3275(internal)X +3579(\(split\))X +3829(nodes)X +4075(are)X +2418 4642(exhausted)N +2763(and)X +2903(an)X +3003(external)X +3286(\(non-split\))X +3648(node)X +3827(is)X +3903(reached.)X +2418 4730(The)N +2571(bucket)X +2813(addresses)X +3149(are)X +3276(stored)X +3500(directly)X +3772(in)X +3861(the)X +3986(exter-)X +2418 4818(nal)N +2536(nodes.)X +2590 4932(Larson's)N +2903(re\256nements)X +3309(are)X +3440(based)X +3655(on)X +3767(the)X +3897(observa-)X +2418 5020(tion)N +2570(that)X +2718(the)X +2844(nodes)X +3059(can)X +3199(be)X +3303(represented)X +3702(by)X +3809(a)X +3872(single)X +4090(bit)X +2418 5108(that)N +2569(is)X +2653(set)X +2773(for)X +2898(internal)X +3174(nodes)X +3392(and)X +3539(not)X +3672(set)X +3791(for)X +3915(external)X +2418 5196(nodes,)N +2652(resulting)X +2959(in)X +3048(a)X +3111(radix)X +3303(search)X +3536(trie.)X +3709(Figure)X +3944(1)X +4010(illus-)X +2418 5284(trates)N +2621(this.)X +2804(Nodes)X +3037(A)X +3123(and)X +3267(B)X +3348(are)X +3475(internal)X +3748(\(split\))X +3967(nodes,)X +2418 5372(thus)N +2573(having)X +2813(no)X +2915(bucket)X +3151(addresses)X +3480(associated)X +3831(with)X +3994(them.)X +2418 5460(Instead,)N +2693(the)X +2814(external)X +3096(nodes)X +3306(\(C,)X +3429(D,)X +3530(and)X +3669(E\))X +3768(each)X +3938(need)X +4112(to)X +2418 5548(refer)N +2594(to)X +2679(a)X +2738(bucket)X +2975(address.)X +3279(These)X +3494(bucket)X +3731(addresses)X +4062(can)X +2418 5636(be)N +2529(stored)X +2760(in)X +2857(the)X +2990(trie)X +3132(itself)X +3327(where)X +3559(the)X +3691(subtries)X +3974(would)X +3 f +432 5960(2)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +3 p +%%Page: 3 3 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +1 f +720 538(live)N +862(if)X +933(they)X +1092(existed)X +1340([KNU68].)X +1709(For)X +1841(example,)X +2154(if)X +2224(nodes)X +2432(F)X +720 626(and)N +858(G)X +938(were)X +1117(the)X +1237(children)X +1522(of)X +1610(node)X +1787(C,)X +1881(the)X +2000(bucket)X +2235(address)X +720 714(L00)N +886(could)X +1101(reside)X +1330(in)X +1429(the)X +1563(bits)X +1714(that)X +1870(will)X +2030(eventually)X +2400(be)X +720 802(used)N +887(to)X +969(store)X +1145(nodes)X +1352(F)X +1416(and)X +1552(G)X +1630(and)X +1766(all)X +1866(their)X +2033(children.)X +10 f +720 890 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +1894 2247(L1)N +784 1925(A)N +1431(E)X +1106 2247(D)N +1428 1281(C)N +1109 1603(B)N +1884 1930(L01)N +1879 1286(L00)N +1221 1814(1)N +903 2131(1)N +1221 1402(0)N +903 1714(0)N +1 Dt +1397 1821 MXY +-8 -32 Dl +-5 19 Dl +-20 6 Dl +33 7 Dl +-187 -182 Dl +1397 1322 MXY +-33 7 Dl +20 6 Dl +5 19 Dl +8 -32 Dl +-187 182 Dl +1069 1639 MXY +-32 7 Dl +20 6 Dl +5 19 Dl +7 -32 Dl +-186 182 Dl +1374 1891 MXY +185 Dc +1779 2133 MXY +0 161 Dl +322 0 Dl +0 -161 Dl +-322 0 Dl +1811 MY +0 161 Dl +322 0 Dl +0 -161 Dl +-322 0 Dl +1166 MY +0 161 Dl +322 0 Dl +0 -161 Dl +-322 0 Dl +1052 2213 MXY +185 Dc +1569 MY +185 Dc +720 1881 MXY +185 Dc +1779 2213 MXY +-28 -17 Dl +10 17 Dl +-10 18 Dl +28 -18 Dl +-543 0 Dl +1769 1891 MXY +-28 -18 Dl +10 18 Dl +-10 18 Dl +28 -18 Dl +-201 0 Dl +1364 1247 MXY +185 Dc +1769 MX +-28 -18 Dl +10 18 Dl +-10 18 Dl +28 -18 Dl +-201 0 Dl +1064 2143 MXY +-7 -32 Dl +-5 19 Dl +-20 6 Dl +32 7 Dl +-181 -181 Dl +3 Dt +-1 Ds +8 s +720 2482(Figure)N +925(1:)X +1 f +1002(Radix)X +1179(search)X +1365(trie)X +1474(with)X +1612(internal)X +1831(nodes)X +2004(A)X +2074(and)X +2189(B,)X +2271(external)X +720 2570(nodes)N +891(C,)X +972(D,)X +1056(and)X +1170(E,)X +1247(and)X +1361(bucket)X +1553(addresses)X +1819(stored)X +1997(in)X +2069(the)X +2168(unused)X +2370(por-)X +720 2658(tion)N +836(of)X +905(the)X +999(trie.)X +10 s +10 f +720 2922 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 f +892 3124(Further)N +1153(simpli\256cations)X +1647(of)X +1738(the)X +1860(above)X +2076([YIG89])X +2377(are)X +720 3212(possible.)N +1038(Using)X +1265(a)X +1337(single)X +1564(radix)X +1765(trie)X +1908(to)X +2006(avoid)X +2219(the)X +2352(\256rst)X +720 3300(hash)N +904(function,)X +1227(replacing)X +1562(the)X +1696(pseudo-random)X +2231(number)X +720 3388(generator)N +1052(with)X +1222(a)X +1286(well)X +1452(designed,)X +1785(bit-randomizing)X +2329(hash)X +720 3476(function,)N +1053(and)X +1215(using)X +1434(the)X +1578(portion)X +1855(of)X +1967(the)X +2110(hash)X +2302(value)X +720 3564(exposed)N +1021(during)X +1268(the)X +1404(trie)X +1549(traversal)X +1864(as)X +1969(a)X +2042(direct)X +2262(bucket)X +720 3652(address)N +990(results)X +1228(in)X +1319(an)X +2 f +1424(access)X +1 f +1663(function)X +1959(that)X +2108(works)X +2333(very)X +720 3740(similar)N +974(to)X +1068(Thompson's)X +1499(algorithm)X +1841(above.)X +2084(The)X +2240(follow-)X +720 3828(ing)N +847(algorithm)X +1183(uses)X +1346(the)X +1469(hash)X +1641(value)X +1840(to)X +1927(traverse)X +2206(a)X +2266(linear-)X +720 3916(ized)N +874(radix)X +1059(trie)X +2 f +8 s +1166 3891(3)N +1 f +10 s +1218 3916(starting)N +1478(at)X +1556(the)X +1674(0)X +2 f +7 s +3884(th)Y +10 s +1 f +1791 3916(bit.)N +0 f +8 s +720 4215(tbit)N +910(=)X +986(0;)X +1296(/*)X +1410(radix)X +1638(trie)X +1828(index)X +2056(*/)X +720 4303(hbit)N +910(=)X +986(0;)X +1296(/*)X +1410(hash)X +1600(bit)X +1752(index)X +2056(*/)X +720 4391(mask)N +910(=)X +986(0;)X +720 4479(hash)N +910(=)X +986 -0.4038(calchash\(key\);)AX +720 4655(for)N +872(\(mask)X +1100(=)X +1176(0;)X +910 4743 -0.4018(isbitset\(tbit\);)AN +910 4831(mask)N +1100(=)X +1176(\(mask)X +1404(<<)X +1518(1\))X +1632(+)X +1708(1\))X +1008 4919(if)N +1122(\(hash)X +1350(&)X +1426(\(1)X +1540(<<)X +1654 -0.4219(hbit++\)\)\))AX +1160 5007(/*)N +1274(right)X +1502(son)X +1692(*/)X +1160 5095(tbit)N +1350(=)X +1426(2)X +1502(*)X +1578(tbit)X +1768(+)X +1844(2;)X +1008 5183(else)N +1 f +16 s +720 5353 MXY +864 0 Dl +2 f +8 s +760 5408(3)N +1 f +9 s +818 5433(A)N +896(linearized)X +1206(radix)X +1380(trie)X +1502(is)X +1576(merely)X +1802(an)X +1895(array)X +2068(representation)X +720 5513(of)N +800(the)X +908(radix)X +1076(search)X +1280(trie)X +1396(described)X +1692(above.)X +1920(The)X +2052(children)X +2308(of)X +2388(the)X +720 5593(node)N +885(with)X +1038(index)X +1223(i)X +1267(can)X +1391(be)X +1483(found)X +1675(at)X +1751(the)X +1863(nodes)X +2055(indexed)X +2307(2*i+1)X +720 5673(and)N +842(2*i+2.)X +0 f +8 s +3146 538(/*)N +3260(left)X +3450(son)X +3678(*/)X +3146 626(tbit)N +3336(=)X +3412(2)X +3488(*)X +3564(tbit)X +3754(+)X +3830(1;)X +2706 802(bucket)N +2972(=)X +3048(hash)X +3238(&)X +3314(mask;)X +2 f +10 s +3495 1167(gdbm)N +1 f +2878 1299(The)N +3027(gdbm)X +3233(\(GNU)X +3458(data)X +3616(base)X +3783(manager\))X +4111(library)X +4349(is)X +4426(a)X +2706 1387(UNIX)N +2933(database)X +3236(manager)X +3539(written)X +3792(by)X +3897(Philip)X +4112(A.)X +4215(Nelson,)X +2706 1475(and)N +2848(made)X +3048(available)X +3364(as)X +3457(a)X +3518(part)X +3668(of)X +3760(the)X +3883(FSF)X +4040(software)X +4342(dis-)X +2706 1563(tribution.)N +3052(The)X +3207(gdbm)X +3419(library)X +3663(provides)X +3969(the)X +4097(same)X +4292(func-)X +2706 1651(tionality)N +3028(of)X +3151(the)X +2 f +3304(dbm)X +1 f +3442(/)X +2 f +3464(ndbm)X +1 f +3697(libraries)X +4015([NEL90])X +4360(but)X +2706 1739(attempts)N +3018(to)X +3121(avoid)X +3340(some)X +3550(of)X +3658(their)X +3846(shortcomings.)X +4337(The)X +2706 1827(gdbm)N +2918(library)X +3162(allows)X +3401(for)X +3525(arbitrary-length)X +4059(data,)X +4242(and)X +4387(its)X +2706 1915(database)N +3027(is)X +3124(a)X +3203(singular,)X +3524(non-sparse)X +2 f +8 s +3872 1890(4)N +1 f +10 s +3947 1915(\256le.)N +4112(The)X +4280(gdbm)X +2706 2003(library)N +2947(also)X +3103(includes)X +2 f +3396(dbm)X +1 f +3560(and)X +2 f +3702(ndbm)X +1 f +3906(compatible)X +4288(inter-)X +2706 2091(faces.)N +2878 2205(The)N +3025(gdbm)X +3229(library)X +3465(is)X +3540(based)X +3745(on)X +2 f +3847(extensible)X +4189(hashing)X +1 f +4442(,)X +2706 2293(a)N +2766(dynamic)X +3066(hashing)X +3339(algorithm)X +3674(by)X +3778(Fagin)X +3984(et)X +4066(al)X +4148([FAG79].)X +2706 2381(This)N +2881(algorithm)X +3225(differs)X +3467(from)X +3655(the)X +3785(previously)X +4155(discussed)X +2706 2469(algorithms)N +3069(in)X +3152(that)X +3293(it)X +3358(uses)X +3517(a)X +2 f +3574(directory)X +1 f +3889(that)X +4030(is)X +4103(a)X +4159(collapsed)X +2706 2557(representation)N +3192([ENB88])X +3517(of)X +3615(the)X +3744(radix)X +3940(search)X +4177(trie)X +4315(used)X +2706 2645(by)N +2 f +2806(sdbm)X +1 f +2975(.)X +10 f +2706 2733 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +7 s +3572 3761(L1)N +1 Dt +3485 3738 MXY +-20 -13 Dl +7 13 Dl +-7 13 Dl +20 -13 Dl +-400 0 Dl +3180 3027 MXY +136 Dc +2706 3494 MXY +136 Dc +2950 3264 MXY +136 Dc +3738 MY +136 Dc +3485 2968 MXY +0 118 Dl +238 0 Dl +0 -118 Dl +-238 0 Dl +3442 MY +0 119 Dl +238 0 Dl +0 -119 Dl +-238 0 Dl +3679 MY +0 119 Dl +238 0 Dl +0 -119 Dl +-238 0 Dl +3187 3501 MXY +136 Dc +2963 3316 MXY +-24 5 Dl +15 4 Dl +4 15 Dl +5 -24 Dl +-137 134 Dl +3204 3083 MXY +-24 5 Dl +15 4 Dl +3 14 Dl +6 -23 Dl +-137 133 Dl +3204 3450 MXY +-6 -24 Dl +-3 14 Dl +-15 5 Dl +24 5 Dl +-137 -134 Dl +2842 3369(0)N +3075 3139(0)N +2842 3676(1)N +3075 3443(1)N +3562 3054(L00)N +3565 3528(L01)N +4197 2968 MXY +0 118 Dl +237 0 Dl +0 -118 Dl +-237 0 Dl +3205 MY +0 119 Dl +237 0 Dl +0 -119 Dl +-237 0 Dl +3561 MY +0 118 Dl +237 0 Dl +0 -118 Dl +-237 0 Dl +3960 2909 MXY +0 237 Dl +118 0 Dl +0 -237 Dl +-118 0 Dl +3146 MY +0 237 Dl +118 0 Dl +0 -237 Dl +-118 0 Dl +3383 MY +0 237 Dl +118 0 Dl +0 -237 Dl +-118 0 Dl +3620 MY +0 237 Dl +118 0 Dl +0 -237 Dl +-118 0 Dl +4197 3027 MXY +-21 -13 Dl +8 13 Dl +-8 13 Dl +21 -13 Dl +-119 0 Dl +4197 3264 MXY +-21 -13 Dl +8 13 Dl +-8 13 Dl +21 -13 Dl +-119 0 Dl +3501 MY +59 0 Dl +0 89 Dl +4078 3738 MXY +59 0 Dl +0 -88 Dl +4197 3590 MXY +-21 -13 Dl +8 13 Dl +-8 13 Dl +21 -13 Dl +-60 0 Dl +4197 3650 MXY +-21 -13 Dl +8 13 Dl +-8 13 Dl +21 -13 Dl +-60 0 Dl +3991 3050(00)N +3991 3287(01)N +3991 3524(10)N +3991 3761(11)N +4269 3050(L00)N +4269 3287(L01)N +4283 3643(L1)N +3485 3501 MXY +-20 -13 Dl +7 13 Dl +-7 13 Dl +20 -13 Dl +-155 0 Dl +3485 3027 MXY +-20 -13 Dl +7 13 Dl +-7 13 Dl +20 -13 Dl +-163 0 Dl +2967 3687 MXY +-5 -24 Dl +-4 14 Dl +-15 4 Dl +24 6 Dl +-141 -141 Dl +3 Dt +-1 Ds +8 s +2706 4033(Figure)N +2903(2:)X +1 f +2972(A)X +3034(radix)X +3181(search)X +3359(trie)X +3460(and)X +3568(a)X +3612(directory)X +3858(representing)X +4189(the)X +4283(trie.)X +10 s +10 f +2706 4209 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 f +2878 4411(In)N +2968(this)X +3106(algorithm,)X +3460(a)X +3519(directory)X +3832(consists)X +4108(of)X +4198(a)X +4256(search)X +2706 4499(trie)N +2847(of)X +2947(depth)X +2 f +3158(n)X +1 f +3211(,)X +3264(containing)X +3635(2)X +2 f +7 s +4467(n)Y +10 s +1 f +3749 4499(bucket)N +3996(addresses)X +4337(\(i.e.)X +2706 4587(each)N +2897(element)X +3194(of)X +3304(the)X +3445(trie)X +3594(is)X +3689(a)X +3767(bucket)X +4023(address\).)X +4373(To)X +2706 4675(access)N +2935(the)X +3056(hash)X +3226(table,)X +3425(a)X +3483(32-bit)X +3696(hash)X +3865(value)X +4061(is)X +4136(calculated)X +2706 4763(and)N +2 f +2861(n)X +1 f +2953(bits)X +3107(of)X +3213(the)X +3350(value)X +3563(are)X +3701(used)X +3886(to)X +3986(index)X +4202(into)X +4364(the)X +2706 4851(directory)N +3018(to)X +3102(obtain)X +3324(a)X +3382(bucket)X +3618(address.)X +3921(It)X +3992(is)X +4067(important)X +4400(to)X +2706 4939(note)N +2866(that)X +3008(multiple)X +3296(entries)X +3532(of)X +3620(this)X +3756(directory)X +4067(may)X +4226(contain)X +2706 5027(the)N +2833(same)X +3026(bucket)X +3268(address)X +3537(as)X +3632(a)X +3696(result)X +3902(of)X +3997(directory)X +4315(dou-)X +2706 5115(bling)N +2903(during)X +3145(bucket)X +3392(splitting.)X +3706(Figure)X +3948(2)X +4021(illustrates)X +4364(the)X +2706 5203(relationship)N +3126(between)X +3436(a)X +3513(typical)X +3772(\(skewed\))X +4108(search)X +4355(trie)X +2706 5291(and)N +2850(its)X +2953(directory)X +3271(representation.)X +3774(The)X +3927(formation)X +4270(of)X +4364(the)X +2706 5379(directory)N +3016(shown)X +3245(in)X +3327(the)X +3445(\256gure)X +3652(is)X +3725(as)X +3812(follows.)X +16 s +2706 5593 MXY +864 0 Dl +2 f +8 s +2746 5648(4)N +1 f +9 s +2796 5673(It)N +2858(does)X +3008(not)X +3118(contain)X +3348(holes.)X +3 f +10 s +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4424(3)X + +4 p +%%Page: 4 4 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +1 f +604 538(Initially,)N +937(there)X +1158(is)X +1271(one)X +1446(slot)X +1620(in)X +1741(the)X +1898(directory)X +432 626(addressing)N +802(a)X +865(single)X +1083(bucket.)X +1364(The)X +1515(depth)X +1719(of)X +1812(the)X +1936(trie)X +2069(is)X +2148(0)X +432 714(and)N +577(0)X +646(bits)X +790(of)X +886(each)X +1063(hash)X +1239(value)X +1442(are)X +1570(examined)X +1910(to)X +2000(deter-)X +432 802(mine)N +624(in)X +718(which)X +946(bucket)X +1192(to)X +1286(place)X +1488(a)X +1556(key;)X +1726(all)X +1837(keys)X +2015(go)X +2126(in)X +432 890(bucket)N +682(0.)X +797(When)X +1024(this)X +1174(bucket)X +1423(is)X +1511(full,)X +1677(its)X +1787(contents)X +2089(are)X +432 978(divided)N +698(between)X +992(L0)X +1107(and)X +1249(L1)X +1363(as)X +1455(was)X +1605(done)X +1786(in)X +1873(the)X +1996(previ-)X +432 1066(ously)N +664(discussed)X +1030(algorithms.)X +1471(After)X +1700(this)X +1874(split,)X +2090(the)X +432 1154(address)N +710(of)X +814(the)X +948(second)X +1207(bucket)X +1457(must)X +1648(be)X +1760(stored)X +1992(in)X +2090(the)X +432 1242(directory.)N +796(To)X +939(accommodate)X +1438(the)X +1589(new)X +1776(address,)X +2090(the)X +432 1330(directory)N +752(is)X +835(split)X +2 f +8 s +972 1305(5)N +1 f +10 s +1330(,)Y +1054(by)X +1163(doubling)X +1476(it,)X +1569(thus)X +1731(increasing)X +2090(the)X +432 1418(depth)N +630(of)X +717(the)X +835(directory)X +1145(by)X +1245(one.)X +604 1532(After)N +813(this)X +967(split,)X +1163(a)X +1237(single)X +1466(bit)X +1588(of)X +1693(the)X +1829(hash)X +2014(value)X +432 1620(needs)N +663(to)X +773(be)X +896(examined)X +1255(to)X +1364(decide)X +1621(whether)X +1927(the)X +2072(key)X +432 1708(belongs)N +711(to)X +803(L0)X +922(or)X +1019(L1.)X +1158(Once)X +1358(one)X +1504(of)X +1601(these)X +1795(buckets)X +2069(\256lls)X +432 1796(\(L0)N +578(for)X +702(example\),)X +1051(it)X +1125(is)X +1208(split)X +1375(as)X +1472(before,)X +1728(and)X +1873(the)X +2000(direc-)X +432 1884(tory)N +585(is)X +662(split)X +823(again)X +1021(to)X +1107(make)X +1305(room)X +1498(for)X +1615(the)X +1736(address)X +2000(of)X +2090(the)X +432 1972(third)N +618(bucket.)X +927(This)X +1104(splitting)X +1400(causes)X +1645(the)X +1778(addresses)X +2121(of)X +432 2060(the)N +567(non-splitting)X +1012(bucket)X +1263(\(L1\))X +1443(to)X +1541(be)X +1653(duplicated.)X +2063(The)X +432 2148(directory)N +766(now)X +948(has)X +1099(four)X +1277(entries,)X +1555(a)X +1635(depth)X +1857(of)X +1968(2,)X +2072(and)X +432 2236(indexes)N +700(the)X +821(buckets)X +1089(L00,)X +1261(L01)X +1413(and)X +1552(L1,)X +1684(as)X +1774(shown)X +2006(in)X +2090(the)X +432 2324(Figure)N +661(2.)X +604 2438(The)N +756(crucial)X +1002(part)X +1154(of)X +1247(the)X +1371(algorithm)X +1708(is)X +1787(the)X +1911(observa-)X +432 2526(tion)N +580(that)X +724(L1)X +837(is)X +914(addressed)X +1255(twice)X +1453(in)X +1539(the)X +1661(directory.)X +1995(If)X +2073(this)X +432 2614(bucket)N +679(were)X +869(to)X +964(split)X +1134(now,)X +1324(the)X +1454(directory)X +1776(already)X +2045(con-)X +432 2702(tains)N +611(room)X +808(to)X +898(hold)X +1067(the)X +1192(address)X +1460(of)X +1554(the)X +1679(new)X +1840(bucket.)X +2121(In)X +432 2790(general,)N +711(the)X +831(relationship)X +1231(between)X +1521(the)X +1641(directory)X +1953(and)X +2090(the)X +432 2878(number)N +704(of)X +798(bucket)X +1039(addresses)X +1374(contained)X +1713(therein)X +1962(is)X +2041(used)X +432 2966(to)N +517(decide)X +750(when)X +947(to)X +1031(split)X +1190(the)X +1310(directory.)X +1662(Each)X +1845(bucket)X +2081(has)X +432 3054(a)N +505(depth,)X +740(\()X +2 f +767(n)X +7 s +3070(b)Y +10 s +1 f +848 3054(\),)N +932(associated)X +1299(with)X +1478(it)X +1558(and)X +1710(appears)X +1992(in)X +2090(the)X +432 3142(directory)N +744(exactly)X +998(2)X +2 f +7 s +3106(n)Y +9 f +1075(-)X +2 f +1106(n)X +4 s +3110(b)Y +7 s +1 f +10 s +1181 3142(times.)N +1396(When)X +1610(a)X +1668(bucket)X +1904(splits,)X +2113(its)X +432 3230(depth)N +638(increases)X +961(by)X +1069(one.)X +1253(The)X +1406(directory)X +1724(must)X +1907(split)X +2072(any)X +432 3318(time)N +602(a)X +665(bucket's)X +964(depth)X +1169(exceeds)X +1451(the)X +1576(depth)X +1781(of)X +1875(the)X +2000(direc-)X +432 3406(tory.)N +630(The)X +784(following)X +1123(code)X +1303(fragment)X +1621(helps)X +1818(to)X +1908(illustrate)X +432 3494(the)N +554(extendible)X +912(hashing)X +1185(algorithm)X +1520([FAG79])X +1838(for)X +1955(access-)X +432 3582(ing)N +554(individual)X +898(buckets)X +1163(and)X +1299(maintaining)X +1701(the)X +1819(directory.)X +0 f +8 s +432 3881(hash)N +622(=)X +698 -0.4038(calchash\(key\);)AX +432 3969(mask)N +622(=)X +698 -0.4018(maskvec[depth];)AX +432 4145(bucket)N +698(=)X +774 -0.4038(directory[hash)AX +1344(&)X +1420(mask];)X +432 4321(/*)N +546(Key)X +698 -0.4219(Insertion)AX +1078(*/)X +432 4409(if)N +546 -0.4038(\(store\(bucket,)AX +1116(key,)X +1306(data\))X +1534(==)X +1648(FAIL\))X +1876({)X +720 4497(newbl)N +948(=)X +1024 -0.4167(getpage\(\);)AX +720 4585 -0.4000(bucket->depth++;)AN +720 4673 -0.4091(newbl->depth)AN +1214(=)X +1290 -0.4038(bucket->depth;)AX +720 4761(if)N +834 -0.4038(\(bucket->depth)AX +1404(>)X +1480(depth\))X +1746({)X +1008 4849(/*)N +1122(double)X +1388 -0.4219(directory)AX +1768(*/)X +1008 4937(depth++;)N +1 f +16 s +432 5033 MXY +864 0 Dl +2 f +8 s +472 5088(5)N +1 f +9 s +534 5113(This)N +692(decision)X +962(to)X +1048(split)X +1202(the)X +1319(directory)X +1608(is)X +1685(based)X +1878(on)X +1979(a)X +2040(com-)X +432 5193(parison)N +666(of)X +748(the)X +858(depth)X +1040(of)X +1121(the)X +1230(page)X +1387(being)X +1568(split)X +1713(and)X +1838(the)X +1947(depth)X +2128(of)X +432 5273(the)N +543(trie.)X +698(In)X +781(Figure)X +992(2,)X +1069(the)X +1180(depths)X +1390(of)X +1472(both)X +1622(L00)X +1760(and)X +1886(L01)X +2024(are)X +2134(2,)X +432 5353(whereas)N +689(the)X +798(depth)X +979(of)X +1060(L1)X +1161(is)X +1230(1.)X +1323(Therefore,)X +1646(if)X +1710(L1)X +1810(were)X +1970(to)X +2046(split,)X +432 5433(the)N +543(directory)X +826(would)X +1029(not)X +1144(need)X +1303(to)X +1382(split.)X +1565(In)X +1648(reality,)X +1872(a)X +1926(bucket)X +2140(is)X +432 5513(allocated)N +727(for)X +846(the)X +969(directory)X +1264(at)X +1351(the)X +1474(time)X +1637(of)X +1732(\256le)X +1858(creation)X +2124(so)X +432 5593(although)N +707(the)X +818(directory)X +1100(splits)X +1274(logically,)X +1566(physical)X +1828(splits)X +2002(do)X +2096(not)X +432 5673(occur)N +610(until)X +760(the)X +866(\256le)X +976(becomes)X +1246(quite)X +1408(large.)X +0 f +8 s +2994 538 -0.4219(directory)AN +3374(=)X +3450 -0.3971(double\(directory\);)AX +2706 626(})N +2706 714 -0.3958(splitbucket\(bucket,)AN +3466(newbl\))X +2706 802(...)N +2418 890(})N +2 f +10 s +3169 1255(hsearch)N +1 f +2590 1387(Since)N +2 f +2807(hsearch)X +1 f +3100(does)X +3286(not)X +3427(have)X +3617(to)X +3717(translate)X +4027(hash)X +2418 1475(values)N +2659(into)X +2819(disk)X +2988(addresses,)X +3352(it)X +3432(can)X +3579(use)X +3721(much)X +3934(simpler)X +2418 1563(algorithms)N +2808(than)X +2994(those)X +3211(de\256ned)X +3495(above.)X +3775(System)X +4058(V's)X +2 f +2418 1651(hsearch)N +1 f +2708(constructs)X +3069(a)X +3141(\256xed-size)X +3489(hash)X +3671(table)X +3862(\(speci\256ed)X +2418 1739(by)N +2519(the)X +2637(user)X +2791(at)X +2869(table)X +3045(creation\).)X +3391(By)X +3504(default,)X +3767(a)X +3823(multiplica-)X +2418 1827(tive)N +2570(hash)X +2748(function)X +3046(based)X +3260(on)X +3371(that)X +3522(described)X +3861(in)X +3954(Knuth,)X +2418 1915(Volume)N +2710(3,)X +2804(section)X +3065(6.4)X +3199([KNU68])X +3541(is)X +3628(used)X +3809(to)X +3905(obtain)X +4138(a)X +2418 2003(primary)N +2694(bucket)X +2930(address.)X +3233(If)X +3309(this)X +3446(bucket)X +3681(is)X +3755(full,)X +3907(a)X +3964(secon-)X +2418 2091(dary)N +2593(multiplicative)X +3069(hash)X +3248(value)X +3454(is)X +3538(computed)X +3885(to)X +3978(de\256ne)X +2418 2179(the)N +2542(probe)X +2751(interval.)X +3062(The)X +3213(probe)X +3422(interval)X +3693(is)X +3772(added)X +3989(to)X +4076(the)X +2418 2267(original)N +2712(bucket)X +2971(address)X +3257(\(modulo)X +3573(the)X +3716(table)X +3916(size\))X +4112(to)X +2418 2355(obtain)N +2658(a)X +2734(new)X +2908(bucket)X +3162(address.)X +3483(This)X +3665(process)X +3946(repeats)X +2418 2443(until)N +2588(an)X +2688(empty)X +2911(bucket)X +3148(is)X +3224(found.)X +3474(If)X +3551(no)X +3654(bucket)X +3891(is)X +3967(found,)X +2418 2531(an)N +2514(insertion)X +2814(fails)X +2972(with)X +3134(a)X +3190(``table)X +3420(full'')X +3605(condition.)X +2590 2645(The)N +2768(basic)X +2986(algorithm)X +3350(may)X +3541(be)X +3670(modi\256ed)X +4006(by)X +4138(a)X +2418 2733(number)N +2705(of)X +2813(compile)X +3112(time)X +3295(options)X +3571(available)X +3902(to)X +4005(those)X +2418 2821(users)N +2604(with)X +2767(AT&T)X +3006(source)X +3237(code.)X +3450(First,)X +3637(the)X +3756(package)X +4040(pro-)X +2418 2909(vides)N +2638(two)X +2809(options)X +3094(for)X +3238(hash)X +3435(functions.)X +3803(Users)X +4036(may)X +2418 2997(specify)N +2690(their)X +2877(own)X +3055(hash)X +3242(function)X +3549(by)X +3669(compiling)X +4032(with)X +2418 3085(``USCR'')N +2757(de\256ned)X +3016(and)X +3155(declaring)X +3477(and)X +3616(de\256ning)X +3901(the)X +4022(vari-)X +2418 3173(able)N +2 f +2578(hcompar)X +1 f +2863(,)X +2909(a)X +2971(function)X +3263(taking)X +3488(two)X +3633(string)X +3840(arguments)X +2418 3261(and)N +2560(returning)X +2880(an)X +2982(integer.)X +3271(Users)X +3480(may)X +3643(also)X +3797(request)X +4054(that)X +2418 3349(hash)N +2587(values)X +2814(be)X +2912(computed)X +3250(simply)X +3489(by)X +3590(taking)X +3811(the)X +3930(modulo)X +2418 3437(of)N +2521(key)X +2673(\(using)X +2909(division)X +3201(rather)X +3424(than)X +3597(multiplication)X +4080(for)X +2418 3525(hash)N +2589(value)X +2787(calculation\).)X +3230(If)X +3308(this)X +3447(technique)X +3783(is)X +3859(used,)X +4049(col-)X +2418 3613(lisions)N +2651(are)X +2775(resolved)X +3072(by)X +3176(scanning)X +3485(sequentially)X +3896(from)X +4076(the)X +2418 3701(selected)N +2702(bucket)X +2941(\(linear)X +3176(probing\).)X +3517(This)X +3684(option)X +3913(is)X +3991(avail-)X +2418 3789(able)N +2572(by)X +2672(de\256ning)X +2954(the)X +3072(variable)X +3351(``DIV'')X +3622(at)X +3700(compile)X +3978(time.)X +2590 3903(A)N +2720(second)X +3015(option,)X +3311(based)X +3565(on)X +3716(an)X +3863(algorithm)X +2418 3991(discovered)N +2787(by)X +2888(Richard)X +3163(P.)X +3248(Brent,)X +3466(rearranges)X +3822(the)X +3940(table)X +4116(at)X +2418 4079(the)N +2549(time)X +2724(of)X +2824(insertion)X +3137(in)X +3232(order)X +3434(to)X +3528(speed)X +3743(up)X +3855(retrievals.)X +2418 4167(The)N +2571(basic)X +2764(idea)X +2926(is)X +3007(to)X +3097(shorten)X +3361(long)X +3531(probe)X +3741(sequences)X +4094(by)X +2418 4255(lengthening)N +2833(short)X +3030(probe)X +3249(sequences.)X +3651(Once)X +3857(the)X +3991(probe)X +2418 4343(chain)N +2613(has)X +2741(exceeded)X +3062(some)X +3252(threshold)X +3571(\(Brent)X +3796(suggests)X +4087(2\),)X +2418 4431(we)N +2541(attempt)X +2809(to)X +2899(shuf\257e)X +3145(any)X +3289(colliding)X +3601(keys)X +3776(\(keys)X +3978(which)X +2418 4519(appeared)N +2734(in)X +2821(the)X +2944(probe)X +3152(sequence)X +3471(of)X +3562(the)X +3684(new)X +3842(key\).)X +4049(The)X +2418 4607(details)N +2652(of)X +2744(this)X +2884(key)X +3025(shuf\257ing)X +3333(can)X +3469(be)X +3569(found)X +3780(in)X +3866([KNU68])X +2418 4695(and)N +2576([BRE73].)X +2946(This)X +3129(algorithm)X +3481(may)X +3660(be)X +3777(obtained)X +4094(by)X +2418 4783(de\256ning)N +2700(the)X +2818(variable)X +3097(``BRENT'')X +3487(at)X +3565(compile)X +3843(time.)X +2590 4897(A)N +2698(third)X +2899(set)X +3038(of)X +3154(options,)X +3458(obtained)X +3783(by)X +3912(de\256ning)X +2418 4985(``CHAINED'',)N +2943(use)X +3086(linked)X +3321(lists)X +3484(to)X +3581(resolve)X +3848(collisions.)X +2418 5073(Either)N +2647(of)X +2747(the)X +2878(primary)X +3164(hash)X +3343(function)X +3642(described)X +3982(above)X +2418 5161(may)N +2584(be)X +2688(used,)X +2882(but)X +3011(all)X +3118(collisions)X +3451(are)X +3577(resolved)X +3876(by)X +3983(build-)X +2418 5249(ing)N +2554(a)X +2623(linked)X +2856(list)X +2986(of)X +3086(entries)X +3333(from)X +3522(the)X +3653(primary)X +3940(bucket.)X +2418 5337(By)N +2542(default,)X +2816(new)X +2981(entries)X +3226(will)X +3381(be)X +3488(added)X +3711(to)X +3804(a)X +3871(bucket)X +4116(at)X +2418 5425(the)N +2541(beginning)X +2886(of)X +2978(the)X +3101(bucket)X +3339(chain.)X +3577(However,)X +3916(compile)X +2418 5513(options)N +2706(``SORTUP'')X +3173(or)X +3293(``SORTDOWN'')X +3908(may)X +4098(be)X +2418 5601(speci\256ed)N +2723(to)X +2805(order)X +2995(the)X +3113(hash)X +3280(chains)X +3505(within)X +3729(each)X +3897(bucket.)X +3 f +432 5960(4)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +5 p +%%Page: 5 5 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +2 f +1444 538(dynahash)N +1 f +892 670(The)N +2 f +1054(dynahash)X +1 f +1398(library,)X +1669(written)X +1932(by)X +2048(Esmond)X +2346(Pitt,)X +720 758(implements)N +1183(Larson's)X +1554(linear)X +1827(hashing)X +2165(algorithm)X +720 846([LAR88])N +1097(with)X +1302(an)X +2 f +1440(hsearch)X +1 f +1756(compatible)X +2174(interface.)X +720 934(Intuitively,)N +1099(a)X +1161(hash)X +1334(table)X +1516(begins)X +1751(as)X +1844(a)X +1905(single)X +2121(bucket)X +2360(and)X +720 1022(grows)N +941(in)X +1028(generations,)X +1443(where)X +1665(a)X +1725(generation)X +2088(corresponds)X +720 1110(to)N +815(a)X +884(doubling)X +1201(in)X +1296(the)X +1427(size)X +1585(of)X +1685(the)X +1815(hash)X +1994(table.)X +2222(The)X +2379(0)X +2 f +7 s +1078(th)Y +10 s +1 f +720 1198(generation)N +1085(occurs)X +1321(as)X +1414(the)X +1538(table)X +1719(grows)X +1940(from)X +2121(one)X +2262(bucket)X +720 1286(to)N +814(two.)X +1006(In)X +1105(the)X +1235(next)X +1405(generation)X +1776(the)X +1906(table)X +2093(grows)X +2320(from)X +720 1374(two)N +862(to)X +946(four.)X +1122(During)X +1371(each)X +1541(generation,)X +1921(every)X +2121(bucket)X +2356(that)X +720 1462(existed)N +967(at)X +1045(the)X +1163(beginning)X +1503(of)X +1590(the)X +1708(generation)X +2067(is)X +2140(split.)X +892 1576(The)N +1041(table)X +1221(starts)X +1414(as)X +1505(a)X +1565(single)X +1780(bucket)X +2018(\(numbered)X +2389(0\),)X +720 1664(the)N +839(current)X +1088(split)X +1245(bucket)X +1479(is)X +1552(set)X +1661(to)X +1743(bucket)X +1977(0,)X +2057(and)X +2193(the)X +2311(max-)X +720 1752(imum)N +933(split)X +1097(point)X +1288(is)X +1368(set)X +1483(to)X +1571(twice)X +1771(the)X +1895(current)X +2149(split)X +2312(point)X +720 1840(\(0\).)N +863(When)X +1084(it)X +1157(is)X +1239(time)X +1410(for)X +1532(a)X +1596(bucket)X +1838(to)X +1928(split,)X +2113(the)X +2239(keys)X +2414(in)X +720 1928(the)N +872(current)X +1154(split)X +1345(bucket)X +1612(are)X +1764(divided)X +2057(between)X +2378(the)X +720 2016(current)N +981(split)X +1151(bucket)X +1397(and)X +1545(a)X +1613(new)X +1779(bucket)X +2025(whose)X +2262(bucket)X +720 2104(number)N +1000(is)X +1088(equal)X +1297(to)X +1394(1)X +1469(+)X +1549(current)X +1812(split)X +1984(bucket)X +2232(+)X +2311(max-)X +720 2192(imum)N +927(split)X +1085(point.)X +1310(We)X +1442(can)X +1574(determine)X +1915(which)X +2131(keys)X +2298(move)X +720 2280(to)N +807(the)X +929(new)X +1087(bucket)X +1325(by)X +1429(examining)X +1791(the)X +2 f +1913(n)X +7 s +1962 2248(th)N +10 s +1 f +2043 2280(bit)N +2151(of)X +2242(a)X +2302(key's)X +720 2368(hash)N +899(value)X +1105(where)X +1334(n)X +1406(is)X +1491(the)X +1620(generation)X +1990(number.)X +2306(After)X +720 2456(the)N +846(bucket)X +1088(at)X +1174(the)X +1300(maximum)X +1651(split)X +1815(point)X +2006(has)X +2140(been)X +2319(split,)X +720 2544(the)N +839(generation)X +1198(number)X +1463(is)X +1536(incremented,)X +1973(the)X +2091(current)X +2339(split)X +720 2632(point)N +908(is)X +985(set)X +1098(back)X +1274(to)X +1360(zero,)X +1543(and)X +1683(the)X +1805(maximum)X +2152(split)X +2312(point)X +720 2720(is)N +815(set)X +946(to)X +1050(the)X +1190(number)X +1477(of)X +1586(the)X +1725(last)X +1877(bucket)X +2132(in)X +2235(the)X +2374(\256le)X +720 2808(\(which)N +971(is)X +1052(equal)X +1253(to)X +1342(twice)X +1543(the)X +1668(old)X +1797(maximum)X +2148(split)X +2312(point)X +720 2896(plus)N +873(1\).)X +892 3010(To)N +1031(facilitate)X +1361(locating)X +1668(keys,)X +1884(we)X +2027(maintain)X +2356(two)X +720 3098(masks.)N +989(The)X +1143(low)X +1291(mask)X +1488(is)X +1569(equal)X +1771(to)X +1861(the)X +1987(maximum)X +2339(split)X +720 3186(bucket)N +967(and)X +1116(the)X +1247(high)X +1422(mask)X +1624(is)X +1710(equal)X +1917(to)X +2011(the)X +2141(next)X +2311(max-)X +720 3274(imum)N +931(split)X +1093(bucket.)X +1372(To)X +1486(locate)X +1703(a)X +1764(speci\256c)X +2033(key,)X +2193(we)X +2311(com-)X +720 3362(pute)N +881(a)X +940(32-bit)X +1154(hash)X +1324(value)X +1520(using)X +1715(a)X +1773(bit-randomizing)X +2311(algo-)X +720 3450(rithm)N +932(such)X +1118(as)X +1224(the)X +1361(one)X +1516(described)X +1862(in)X +1962([LAR88].)X +2334(This)X +720 3538(hash)N +893(value)X +1093(is)X +1172(then)X +1336(masked)X +1607(with)X +1775(the)X +1898(high)X +2065(mask.)X +2299(If)X +2378(the)X +720 3626(resulting)N +1026(number)X +1297(is)X +1376(greater)X +1626(than)X +1790(the)X +1913(maximum)X +2262(bucket)X +720 3714(in)N +823(the)X +962(table)X +1159(\(current)X +1455(split)X +1633(bucket)X +1888(+)X +1974(maximum)X +2339(split)X +720 3802(point\),)N +962(the)X +1091(hash)X +1269(value)X +1474(is)X +1558(masked)X +1834(with)X +2007(the)X +2136(low)X +2287(mask.)X +720 3890(In)N +825(either)X +1046(case,)X +1242(the)X +1377(result)X +1592(of)X +1696(the)X +1831(mask)X +2037(is)X +2127(the)X +2262(bucket)X +720 3978(number)N +989(for)X +1107(the)X +1229(given)X +1431(key.)X +1611(The)X +1759(algorithm)X +2093(below)X +2312(illus-)X +720 4066(trates)N +914(this)X +1049(process.)X +0 f +8 s +720 4365(h)N +796(=)X +872 -0.4038(calchash\(key\);)AX +720 4453(bucket)N +986(=)X +1062(h)X +1138(&)X +1214 -0.4167(high_mask;)AX +720 4541(if)N +834(\()X +910(bucket)X +1176(>)X +1252 -0.4167(max_bucket)AX +1670(\))X +1008 4629(bucket)N +1274(=)X +1350(h)X +1426(&)X +1502 -0.4219(low_mask;)AX +720 4717 -0.4018(return\(bucket\);)AN +1 f +10 s +892 5042(In)N +1013(order)X +1237(to)X +1353(decide)X +1617(when)X +1845(to)X +1961(split)X +2152(a)X +2242(bucket,)X +2 f +720 5130(dynahash)N +1 f +1050(uses)X +2 f +1210(controlled)X +1561(splitting)X +1 f +1822(.)X +1884(A)X +1964(hash)X +2133(table)X +2311(has)X +2440(a)X +720 5218(\256ll)N +837(factor)X +1054(which)X +1279(is)X +1361(expressed)X +1707(in)X +1798(terms)X +2004(of)X +2099(the)X +2225(average)X +720 5306(number)N +990(of)X +1082(keys)X +1253(in)X +1339(each)X +1511(bucket.)X +1789(Each)X +1974(time)X +2140(the)X +2262(table's)X +720 5394(total)N +885(number)X +1153(of)X +1243(keys)X +1413(divided)X +1676(by)X +1778(its)X +1875(number)X +2142(of)X +2231(buckets)X +720 5482(exceeds)N +995(this)X +1130(\256ll)X +1238(factor,)X +1466(a)X +1522(bucket)X +1756(is)X +1829(split.)X +2878 538(Since)N +3079(the)X +2 f +3200(hsearch)X +1 f +3477(create)X +3693(interface)X +3998(\()X +2 f +4025(hcreate)X +1 f +4266(\))X +4315(calls)X +2706 626(for)N +2842(an)X +2960(estimate)X +3269(of)X +3378(the)X +3518(\256nal)X +3702(size)X +3869(of)X +3978(the)X +4118(hash)X +4306(table)X +2706 714(\()N +2 f +2733(nelem)X +1 f +2925(\),)X +2 f +3007(dynahash)X +1 f +3349(uses)X +3522(this)X +3672(information)X +4085(to)X +4182(initialize)X +2706 802(the)N +2848(table.)X +3088(The)X +3257(initial)X +3486(number)X +3774(of)X +3884(buckets)X +4172(is)X +4268(set)X +4400(to)X +2 f +2706 890(nelem)N +1 f +2926(rounded)X +3217(to)X +3306(the)X +3431(next)X +3596(higher)X +3828(power)X +4056(of)X +4150(two.)X +4337(The)X +2706 978(current)N +2958(split)X +3118(point)X +3305(is)X +3381(set)X +3493(to)X +3578(0)X +3641(and)X +3780(the)X +3901(maximum)X +4248(bucket)X +2706 1066(and)N +2842(maximum)X +3186(split)X +3343(point)X +3527(are)X +3646(set)X +3755(to)X +3837(this)X +3972(rounded)X +4255(value.)X +3 f +3148 1220(The)N +3301(New)X +3473(Implementation)X +1 f +2878 1352(Our)N +3042(implementation)X +3583(is)X +3675(also)X +3842(based)X +4063(on)X +4181(Larson's)X +2706 1440(linear)N +2939(hashing)X +3238([LAR88])X +3582(algorithm)X +3943(as)X +4060(well)X +4248(as)X +4364(the)X +2 f +2706 1528(dynahash)N +1 f +3047(implementation.)X +3623(The)X +2 f +3782(dbm)X +1 f +3954(family)X +4197(of)X +4297(algo-)X +2706 1616(rithms)N +2942(decide)X +3184(dynamically)X +3612(which)X +3840(bucket)X +4085(to)X +4178(split)X +4346(and)X +2706 1704(when)N +2914(to)X +3010(split)X +3180(it)X +3257(\(when)X +3491(it)X +3568(over\257ows\))X +3944(while)X +2 f +4155(dynahash)X +1 f +2706 1792(splits)N +2933(in)X +3054(a)X +3149(prede\256ned)X +3547(order)X +3776(\(linearly\))X +4134(and)X +4309(at)X +4426(a)X +2706 1880(prede\256ned)N +3116(time)X +3328(\(when)X +3599(the)X +3767(table)X +3993(\256ll)X +4151(factor)X +4409(is)X +2706 1968(exceeded\).)N +3121(We)X +3280(use)X +3434(a)X +3517(hybrid)X +3773(of)X +3887(these)X +4099(techniques.)X +2706 2056(Splits)N +2913(occur)X +3118(in)X +3206(the)X +3330(prede\256ned)X +3695(order)X +3891(of)X +3984(linear)X +4193(hashing,)X +2706 2144(but)N +2845(the)X +2980(time)X +3159(at)X +3253(which)X +3485(pages)X +3704(are)X +3839(split)X +4012(is)X +4101(determined)X +2706 2232(both)N +2869(by)X +2970(page)X +3143(over\257ows)X +3480(\()X +2 f +3507(uncontrolled)X +3937(splitting)X +1 f +4198(\))X +4246(and)X +4382(by)X +2706 2320(exceeding)N +3052(the)X +3170(\256ll)X +3278(factor)X +3486(\()X +2 f +3513(controlled)X +3862(splitting)X +1 f +4123(\))X +2878 2434(A)N +2962(hash)X +3135(table)X +3317(is)X +3395(parameterized)X +3876(by)X +3981(both)X +4148(its)X +4248(bucket)X +2706 2522(size)N +2904(\()X +2 f +2931(bsize)X +1 f +(\))S +3191(and)X +3380(\256ll)X +3541(factor)X +3801(\()X +2 f +3828(ffactor)X +1 f +4041(\).)X +4180(Whereas)X +2 f +2706 2610(dynahash's)N +1 f +3095(buckets)X +3364(can)X +3500(be)X +3599(represented)X +3993(as)X +4083(a)X +4142(linked)X +4365(list)X +2706 2698(of)N +2798(elements)X +3108(in)X +3195(memory,)X +3507(our)X +3639(package)X +3928(needs)X +4136(to)X +4222(support)X +2706 2786(disk)N +2874(access,)X +3135(and)X +3286(must)X +3476(represent)X +3806(buckets)X +4086(in)X +4183(terms)X +4395(of)X +2706 2874(pages.)N +2955(The)X +2 f +3106(bsize)X +1 f +3291(is)X +3369(the)X +3492(size)X +3642(\(in)X +3756(bytes\))X +3977(of)X +4069(these)X +4259(pages.)X +2706 2962(As)N +2833(in)X +2933(linear)X +3154(hashing,)X +3461(the)X +3597(number)X +3879(of)X +3983(buckets)X +4265(in)X +4364(the)X +2706 3050(table)N +2906(is)X +3003(equal)X +3221(to)X +3327(the)X +3469(number)X +3758(of)X +3869(keys)X +4060(in)X +4165(the)X +4306(table)X +2706 3138(divided)N +2988(by)X +2 f +3110(ffactor)X +1 f +3323(.)X +2 f +8 s +3113(6)Y +1 f +10 s +3417 3138(The)N +3584(controlled)X +3950(splitting)X +4252(occurs)X +2706 3226(each)N +2878(time)X +3044(the)X +3166(number)X +3435(of)X +3526(keys)X +3697(in)X +3783(the)X +3905(table)X +4085(exceeds)X +4364(the)X +2706 3314(\256ll)N +2814(factor)X +3022(multiplied)X +3370(by)X +3470(the)X +3588(number)X +3853(of)X +3940(buckets.)X +2878 3428(Inserting)N +3187(keys)X +3358(and)X +3498(splitting)X +3783(buckets)X +4051(is)X +4127(performed)X +2706 3516(precisely)N +3018(as)X +3107(described)X +3437(previously)X +3796(for)X +2 f +3911(dynahash)X +1 f +4218(.)X +4279(How-)X +2706 3604(ever,)N +2897(since)X +3094(buckets)X +3371(are)X +3502(now)X +3671(comprised)X +4036(of)X +4134(pages,)X +4368(we)X +2706 3692(must)N +2883(be)X +2981(prepared)X +3284(to)X +3367(handle)X +3602(cases)X +3793(where)X +4011(the)X +4130(size)X +4276(of)X +4364(the)X +2706 3780(keys)N +2873(and)X +3009(data)X +3163(in)X +3245(a)X +3301(bucket)X +3535(exceed)X +3779(the)X +3897(bucket)X +4131(size.)X +3 f +3318 3934(Over\257ow)N +3654(Pages)X +1 f +2878 4066(There)N +3095(are)X +3223(two)X +3372(cases)X +3571(where)X +3797(a)X +3862(key)X +4007(may)X +4174(not)X +4305(\256t)X +4400(in)X +2706 4154(its)N +2802(designated)X +3166(bucket.)X +3441(In)X +3529(the)X +3647(\256rst)X +3791(case,)X +3970(the)X +4088(total)X +4250(size)X +4395(of)X +2706 4242(the)N +2833(key)X +2978(and)X +3123(data)X +3286(may)X +3453(exceed)X +3706(the)X +3833(bucket)X +4076(size.)X +4269(In)X +4364(the)X +2706 4330(second,)N +3008(addition)X +3328(of)X +3453(a)X +3547(new)X +3739(key)X +3913(could)X +4149(cause)X +4386(an)X +2706 4418(over\257ow,)N +3068(but)X +3227(the)X +3382(bucket)X +3652(in)X +3770(question)X +4097(is)X +4206(not)X +4364(yet)X +2706 4506(scheduled)N +3049(to)X +3133(be)X +3230(split.)X +3428(In)X +3516(existing)X +3790(implementations,)X +4364(the)X +2706 4594(second)N +2953(case)X +3115(never)X +3317(arises)X +3523(\(since)X +3738(buckets)X +4006(are)X +4128(split)X +4288(when)X +2706 4682(they)N +2871(over\257ow\))X +3210(and)X +3352(the)X +3476(\256rst)X +3626(case)X +3791(is)X +3870(not)X +3998(handled)X +4278(at)X +4362(all.)X +2706 4770(Although)N +3036(large)X +3225(key/data)X +3525(pair)X +3678(handling)X +3986(is)X +4066(dif\256cult)X +4346(and)X +2706 4858(expensive,)N +3083(it)X +3163(is)X +3252(essential.)X +3604(In)X +3706(a)X +3777(linear)X +3995(hashed)X +4253(imple-)X +2706 4946(mentation,)N +3087(over\257ow)X +3413(pages)X +3636(are)X +3775(required)X +4083(for)X +4217(buckets)X +2706 5034(which)N +2935(over\257ow)X +3253(before)X +3492(they)X +3662(are)X +3793(split,)X +3982(so)X +4085(we)X +4211(can)X +4355(use)X +2706 5122(the)N +2833(same)X +3027(mechanism)X +3421(for)X +3544(large)X +3734(key/data)X +4035(pairs)X +4220(that)X +4368(we)X +2706 5210(use)N +2837(for)X +2955(over\257ow)X +3264(pages.)X +3511(Logically,)X +3862(we)X +3980(chain)X +4177(over\257ow)X +16 s +2706 5353 MXY +864 0 Dl +2 f +8 s +2746 5408(6)N +1 f +9 s +2801 5433(This)N +2952(is)X +3023(not)X +3138(strictly)X +3361(true.)X +3532(The)X +3667(\256le)X +3782(does)X +3937(not)X +4052(contract)X +4306(when)X +2706 5513(keys)N +2861(are)X +2972(deleted,)X +3221(so)X +3308(the)X +3419(number)X +3662(of)X +3744(buckets)X +3986(is)X +4056(actually)X +4306(equal)X +2706 5593(to)N +2782(the)X +2890(maximum)X +3202(number)X +3441(of)X +3520(keys)X +3671(ever)X +3814(present)X +4041(in)X +4116(the)X +4223(table)X +4382(di-)X +2706 5673(vided)N +2884(by)X +2974(the)X +3080(\256ll)X +3178(factor.)X +3 f +10 s +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4424(5)X + +6 p +%%Page: 6 6 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +1 f +432 538(pages)N +639(to)X +725(the)X +847(buckets)X +1116(\(also)X +1296(called)X +1512(primary)X +1789(pages\).)X +2062(In)X +2152(a)X +432 626(memory)N +730(based)X +943(representation,)X +1448(over\257ow)X +1763(pages)X +1976(do)X +2086(not)X +432 714(pose)N +628(any)X +792(special)X +1063(problems)X +1409(because)X +1712(we)X +1854(can)X +2014(chain)X +432 802(over\257ow)N +776(pages)X +1017(to)X +1137(primary)X +1449(pages)X +1690(using)X +1921(memory)X +432 890(pointers.)N +776(However,)X +1137(mapping)X +1463(these)X +1674(over\257ow)X +2005(pages)X +432 978(into)N +584(a)X +648(disk)X +809(\256le)X +939(is)X +1019(more)X +1211(of)X +1305(a)X +1368(challenge,)X +1723(since)X +1915(we)X +2036(need)X +432 1066(to)N +547(be)X +675(able)X +861(to)X +975(address)X +1268(both)X +1462(bucket)X +1728(pages,)X +1983(whose)X +432 1154(numbers)N +729(are)X +849(growing)X +1137(linearly,)X +1422(and)X +1558(some)X +1747(indeterminate)X +432 1242(number)N +715(of)X +820(over\257ow)X +1143(pages)X +1364(without)X +1646(reorganizing)X +2090(the)X +432 1330(\256le.)N +604 1444(One)N +789(simple)X +1053(solution)X +1361(would)X +1612(be)X +1739(to)X +1852(allocate)X +2152(a)X +432 1532(separate)N +737(\256le)X +880(for)X +1015(over\257ow)X +1341(pages.)X +1604(The)X +1769(disadvantage)X +432 1620(with)N +605(such)X +783(a)X +850(technique)X +1193(is)X +1276(that)X +1426(it)X +1500(requires)X +1789(an)X +1895(extra)X +2086(\256le)X +432 1708(descriptor,)N +794(an)X +891(extra)X +1073(system)X +1316(call)X +1453(on)X +1554(open)X +1731(and)X +1867(close,)X +2072(and)X +432 1796(logically)N +739(associating)X +1122(two)X +1269(independent)X +1687(\256les.)X +1886(For)X +2023(these)X +432 1884(reasons,)N +728(we)X +857(wanted)X +1123(to)X +1219(map)X +1391(both)X +1567(primary)X +1855(pages)X +2072(and)X +432 1972(over\257ow)N +737(pages)X +940(into)X +1084(the)X +1202(same)X +1387(\256le)X +1509(space.)X +604 2086(The)N +799(buddy-in-waiting)X +1425(algorithm)X +1806(provides)X +2152(a)X +432 2174(mechanism)N +851(to)X +966(support)X +1259(multiple)X +1578(pages)X +1814(per)X +1970(logical)X +432 2262(bucket)N +685(while)X +902(retaining)X +1226(the)X +1362(simple)X +1613(split)X +1788(sequence)X +2121(of)X +432 2350(linear)N +681(hashing.)X +1015(Over\257ow)X +1383(pages)X +1631(are)X +1795(preallocated)X +432 2438(between)N +781(generations)X +1232(of)X +1379(primary)X +1713(pages.)X +1996(These)X +432 2526(over\257ow)N +759(pages)X +984(are)X +1125(used)X +1314(by)X +1436(any)X +1594(bucket)X +1850(containing)X +432 2614(more)N +646(keys)X +842(than)X +1029(\256t)X +1144(on)X +1273(the)X +1420(primary)X +1723(page)X +1924(and)X +2089(are)X +432 2702(reclaimed,)N +808(if)X +896(possible,)X +1217(when)X +1430(the)X +1567(bucket)X +1819(later)X +2000(splits.)X +432 2790(Figure)N +687(3)X +773(depicts)X +1045(the)X +1188(layout)X +1433(of)X +1545(primary)X +1844(pages)X +2072(and)X +432 2878(over\257ow)N +752(pages)X +970(within)X +1209(the)X +1342(same)X +1542(\256le.)X +1699(Over\257ow)X +2036(page)X +432 2966(use)N +586(information)X +1011(is)X +1111(recorded)X +1440(in)X +1548(bitmaps)X +1847(which)X +2089(are)X +432 3054(themselves)N +819(stored)X +1046(on)X +1157(over\257ow)X +1472(pages.)X +1725(The)X +1880(addresses)X +432 3142(of)N +520(the)X +639(bitmap)X +882(pages)X +1086(and)X +1223(the)X +1342(number)X +1608(of)X +1695(pages)X +1898(allocated)X +432 3230(at)N +515(each)X +688(split)X +850(point)X +1039(are)X +1163(stored)X +1384(in)X +1470(the)X +1592(\256le)X +1718(header.)X +1997(Using)X +432 3318(this)N +577(information,)X +1005(both)X +1177(over\257ow)X +1492(addresses)X +1829(and)X +1974(bucket)X +432 3406(addresses)N +764(can)X +900(be)X +999(mapped)X +1276(to)X +1361(disk)X +1517(addresses)X +1848(by)X +1951(the)X +2072(fol-)X +432 3494(lowing)N +674(calculation:)X +0 f +8 s +432 3793(int)N +736(bucket;)X +1192(/*)X +1306(bucket)X +1572(address)X +1876(*/)X +432 3881(u_short)N +736(oaddr;)X +1192(/*)X +1306(OVERFLOW)X +1648(address)X +1952(*/)X +432 3969(int)N +736 -0.4125(nhdr_pages;)AX +1192(/*)X +1306(npages)X +1572(in)X +1686 -112.4062(\256le)AX +1838(header)X +2104(*/)X +432 4057(int)N +736 -0.4125(spares[32];)AX +1192(/*)X +1306(npages)X +1572(at)X +1686(each)X +1876(split)X +2104(*/)X +432 4145(int)N +736(log2\(\);)X +1198(/*)X +1312(ceil\(log)X +1654(base)X +1844(2\))X +1958(*/)X +432 4321(#DEFINE)N +736 -0.3929(BUCKET_TO_PAGE\(bucket\))AX +1610(\\)X +584 4409(bucket)N +850(+)X +926 -0.4167(nhdr_pages)AX +1344(+)X +1420(\\)X +584 4497 -0.3894(\(bucket?spares[logs2\(bucket)AN +1648(+)X +1724(1\)-1]:0\))X +432 4673(#DEFINE)N +736 -0.3947(OADDR_TO_PAGE\(oaddr\))AX +1534(\\)X +584 4761 -0.3984(BUCKET_TO_PAGE\(\(1)AN +1268(<<)X +1382 -0.4091(\(oaddr>>11\)\))AX +1876(-)X +1952(1\))X +2066(+)X +2142(\\)X +584 4849(oaddr)N +812(&)X +888(0x7ff;)X +1 f +10 s +604 5262(An)N +728(over\257ow)X +1039(page)X +1217(is)X +1295(addressed)X +1637(by)X +1742(its)X +1842(split)X +2004(point,)X +432 5350(identifying)N +858(the)X +1031(generations)X +1476(between)X +1819(which)X +2090(the)X +432 5438(over\257ow)N +740(page)X +915(is)X +991(allocated,)X +1324(and)X +1463(its)X +1561(page)X +1736(number,)X +2023(iden-)X +432 5526(tifying)N +665(the)X +783(particular)X +1111(page)X +1283(within)X +1507(the)X +1625(split)X +1782(point.)X +1986(In)X +2073(this)X +432 5614(implementation,)N +983(offsets)X +1225(within)X +1457(pages)X +1668(are)X +1795(16)X +1903(bits)X +2046(long)X +432 5702(\(limiting)N +732(the)X +851(maximum)X +1196(page)X +1368(size)X +1513(to)X +1595(32K\),)X +1800(so)X +1891(we)X +2005(select)X +2418 538(an)N +2535(over\257ow)X +2860(page)X +3052(addressing)X +3435(algorithm)X +3786(that)X +3946(can)X +4098(be)X +2418 626(expressed)N +2760(in)X +2847(16)X +2952(bits)X +3091(and)X +3231(which)X +3451(allows)X +3684(quick)X +3886(retrieval.)X +2418 714(The)N +2568(top)X +2695(\256ve)X +2840(bits)X +2980(indicate)X +3258(the)X +3380(split)X +3541(point)X +3729(and)X +3869(the)X +3991(lower)X +2418 802(eleven)N +2650(indicate)X +2926(the)X +3046(page)X +3220(number)X +3487(within)X +3713(the)X +3832(split)X +3990(point.)X +2418 890(Since)N +2633(\256ve)X +2789(bits)X +2940(are)X +3075(reserved)X +3384(for)X +3514(the)X +3648(split)X +3821(point,)X +4041(\256les)X +2418 978(may)N +2578(split)X +2737(32)X +2839(times)X +3034(yielding)X +3318(a)X +3376(maximum)X +3721(\256le)X +3844(size)X +3990(of)X +4078(2)X +7 s +946(32)Y +10 s +2418 1066(buckets)N +2698(and)X +2849(32)X +2 f +(*)S +1 f +2982(2)X +7 s +1034(11)Y +10 s +3113 1066(over\257ow)N +3433(pages.)X +3691(The)X +3850(maximum)X +2418 1154(page)N +2597(size)X +2749(is)X +2829(2)X +7 s +1122(15)Y +10 s +1154(,)Y +2971(yielding)X +3259(a)X +3321(maximum)X +3671(\256le)X +3799(size)X +3950(greater)X +2418 1242(than)N +2601(131,000)X +2906(GB)X +3061(\(on)X +3212(\256le)X +3358(systems)X +3655(supporting)X +4041(\256les)X +2418 1330(larger)N +2626(than)X +2784(4GB\).)X +10 f +2418 1418 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 Dt +4014 2275 MXY +0 133 Dl +3881 2275 MXY +0 133 Dl +3748 2275 MXY +0 133 Dl +3083 2275 MXY +0 133 Dl +5 s +1 f +3523 2475(2/3)N +3390(2/2)X +3257(2/1)X +2859(1/2)X +2726(1/1)X +5 Dt +3814 1743 MXY +0 133 Dl +3282 1743 MXY +0 133 Dl +3017 1743 MXY +0 133 Dl +2884 1743 MXY +0 133 Dl +1 Dt +3681 1743 MXY +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3548 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3415 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3282 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3150 MX +0 133 Dl +132 0 Dl +0 -133 Dl +-132 0 Dl +3017 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +2884 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3 f +8 s +3017 2601(Over\257ow)N +3285(Addresses)X +3515 2833(Over\257ow)N +3783(Pages)X +2850(Buckets)X +1 Di +3349 2740 MXY + 3349 2740 lineto + 3482 2740 lineto + 3482 2873 lineto + 3349 2873 lineto + 3349 2740 lineto +closepath 3 3349 2740 3482 2873 Dp +2684 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +5 Dt +4146 2275 MXY +0 133 Dl +3216 2275 MXY +0 133 Dl +2684 2275 MXY +0 133 Dl +2551 2275 MXY +0 133 Dl +1 f +3798 1963(3)N +3266 1980(2)N +3001(1)X +2868(0)X +1 Dt +2751 1743 MXY +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3548 2275 MXY +-15 -22 Dl +2 16 Dl +-13 11 Dl +26 -5 Dl +-282 -117 Dl +3432 2275 MXY +-10 -25 Dl +-2 16 Dl +-15 8 Dl +27 1 Dl +-166 -117 Dl +3282 2275 MXY +12 -25 Dl +-14 10 Dl +-15 -6 Dl +17 21 Dl +-16 -117 Dl +2884 2275 MXY +26 7 Dl +-12 -12 Dl +3 -16 Dl +-17 21 Dl +382 -117 Dl +2751 2275 MXY +25 9 Dl +-11 -12 Dl +5 -17 Dl +-19 20 Dl +515 -117 Dl +3 f +3070 2152(Over\257ow)N +3338(Pages)X +3482 2275 MXY + 3482 2275 lineto + 3615 2275 lineto + 3615 2408 lineto + 3482 2408 lineto + 3482 2275 lineto +closepath 3 3482 2275 3615 2408 Dp +3349 MX + 3349 2275 lineto + 3482 2275 lineto + 3482 2408 lineto + 3349 2408 lineto + 3349 2275 lineto +closepath 3 3349 2275 3482 2408 Dp +3216 MX + 3216 2275 lineto + 3349 2275 lineto + 3349 2408 lineto + 3216 2408 lineto + 3216 2275 lineto +closepath 3 3216 2275 3349 2408 Dp +2817 MX + 2817 2275 lineto + 2950 2275 lineto + 2950 2408 lineto + 2817 2408 lineto + 2817 2275 lineto +closepath 3 2817 2275 2950 2408 Dp +2684 MX + 2684 2275 lineto + 2817 2275 lineto + 2817 2408 lineto + 2684 2408 lineto + 2684 2275 lineto +closepath 3 2684 2275 2817 2408 Dp +3615 MX +0 133 Dl +531 0 Dl +0 -133 Dl +-531 0 Dl +2950 MX +0 133 Dl +266 0 Dl +0 -133 Dl +-266 0 Dl +2551 MX +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3798 1726 MXY +-21 -18 Dl +6 16 Dl +-10 13 Dl +25 -11 Dl +-599 -99 Dl +3266 1726 MXY +-1 -27 Dl +-7 15 Dl +-17 1 Dl +25 11 Dl +-67 -99 Dl +3033 1726 MXY +27 1 Dl +-14 -8 Dl +-1 -17 Dl +-12 24 Dl +166 -99 Dl +2900 1726 MXY +27 7 Dl +-13 -11 Dl +3 -17 Dl +-17 21 Dl +299 -99 Dl +3058 1621(Split)N +3203(Points)X +2418 2275 MXY +0 133 Dl +133 0 Dl +0 -133 Dl +-133 0 Dl +3 Dt +-1 Ds +3137(Figure)Y +2619(3:)X +1 f +2691(Split)X +2832(points)X +3008(occur)X +3168(between)X +3399(generations)X +3712(and)X +3823(are)X +3919(numbered)X +2418 3225(from)N +2560(0.)X +2642(In)X +2713(this)X +2824(\256gure)X +2991(there)X +3136(are)X +3231(two)X +3345(over\257ow)X +3590(pages)X +3753(allocated)X +4000(at)X +4063(split)X +2418 3313(point)N +2566(1)X +2614(and)X +2722(three)X +2865(allocated)X +3111(at)X +3173(split)X +3300(point)X +3448(2.)X +10 s +10 f +2418 3489 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +2949 3731(Buffer)N +3192(Management)X +1 f +2590 3863(The)N +2744(hash)X +2920(table)X +3105(is)X +3187(stored)X +3412(in)X +3502(memory)X +3797(as)X +3892(a)X +3956(logical)X +2418 3951(array)N +2633(of)X +2749(bucket)X +3012(pointers.)X +3359(Physically,)X +3761(the)X +3907(array)X +4121(is)X +2418 4039(arranged)N +2728(in)X +2818(segments)X +3144(of)X +3239(256)X +3387(pointers.)X +3713(Initially,)X +4013(there)X +2418 4127(is)N +2530(space)X +2767(to)X +2887(allocate)X +3195(256)X +3373(segments.)X +3769(Reallocation)X +2418 4215(occurs)N +2651(when)X +2847(the)X +2967(number)X +3234(of)X +3323(buckets)X +3590(exceeds)X +3867(32K)X +4027(\(256)X +2418 4303(*)N +2508(256\).)X +2745(Primary)X +3053(pages)X +3286(may)X +3473(be)X +3598(accessed)X +3929(directly)X +2418 4391(through)N +2711(the)X +2853(array)X +3062(by)X +3185(bucket)X +3442(number)X +3730(and)X +3889(over\257ow)X +2418 4479(pages)N +2628(are)X +2754 0.4028(referenced)AX +3122(logically)X +3429(by)X +3536(their)X +3710(over\257ow)X +4022(page)X +2418 4567(address.)N +2726(For)X +2864(small)X +3063(hash)X +3236(tables,)X +3469(it)X +3539(is)X +3618(desirable)X +3934(to)X +4022(keep)X +2418 4655(all)N +2525(pages)X +2735(in)X +2823(main)X +3009(memory)X +3302(while)X +3506(on)X +3612(larger)X +3826(tables,)X +4059(this)X +2418 4743(is)N +2523(probably)X +2860(impossible.)X +3298(To)X +3438(satisfy)X +3698(both)X +3891(of)X +4009(these)X +2418 4831(requirements,)N +2900(the)X +3041(package)X +3348(includes)X +3658(buffer)X +3897(manage-)X +2418 4919(ment)N +2598(with)X +2760(LRU)X +2940(\(least)X +3134(recently)X +3413(used\))X +3607(replacement.)X +2590 5033(By)N +2730(default,)X +3020(the)X +3165(package)X +3475(allocates)X +3802(up)X +3928(to)X +4036(64K)X +2418 5121(bytes)N +2616(of)X +2712(buffered)X +3014(pages.)X +3246(All)X +3377(pages)X +3589(in)X +3680(the)X +3807(buffer)X +4032(pool)X +2418 5209(are)N +2542(linked)X +2766(in)X +2852(LRU)X +3036(order)X +3230(to)X +3316(facilitate)X +3621(fast)X +3761(replacement.)X +2418 5297(Whereas)N +2724(ef\256cient)X +3011(access)X +3241(to)X +3327(primary)X +3605(pages)X +3812(is)X +3889(provided)X +2418 5385(by)N +2521(the)X +2642(bucket)X +2879(array,)X +3087(ef\256cient)X +3372(access)X +3600(to)X +3684(over\257ow)X +3991(pages)X +2418 5473(is)N +2501(provided)X +2816(by)X +2926(linking)X +3182(over\257ow)X +3497(page)X +3679(buffers)X +3936(to)X +4027(their)X +2418 5561(predecessor)N +2827(page)X +3008(\(either)X +3247(the)X +3374(primary)X +3657(page)X +3838(or)X +3933(another)X +2418 5649(over\257ow)N +2742(page\).)X +3000(This)X +3181(means)X +3425(that)X +3584(an)X +3699(over\257ow)X +4022(page)X +3 f +432 5960(6)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +7 p +%%Page: 7 7 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +1 f +720 538(cannot)N +955(be)X +1052(present)X +1305(in)X +1388(the)X +1507(buffer)X +1724(pool)X +1886(if)X +1955(its)X +2050(primary)X +2324(page)X +720 626(is)N +804(not)X +937(present.)X +1240(This)X +1413(does)X +1591(not)X +1724(impact)X +1972(performance)X +2409(or)X +720 714(functionality,)N +1209(because)X +1524(an)X +1660(over\257ow)X +2005(page)X +2217(will)X +2400(be)X +720 802(accessed)N +1048(only)X +1236(after)X +1430(its)X +1550(predecessor)X +1975(page)X +2172(has)X +2324(been)X +720 890(accessed.)N +1068(Figure)X +1303(4)X +1369(depicts)X +1622(the)X +1746(data)X +1905(structures)X +2242(used)X +2414(to)X +720 978(manage)N +990(the)X +1108(buffer)X +1325(pool.)X +892 1092(The)N +1040(in-memory)X +1419(bucket)X +1656(array)X +1845(contains)X +2134(pointers)X +2414(to)X +720 1180(buffer)N +975(header)X +1248(structures)X +1617(which)X +1870(represent)X +2222(primary)X +720 1268(pages.)N +968(Buffer)X +1203(headers)X +1474(contain)X +1735(modi\256ed)X +2043(bits,)X +2202(the)X +2324(page)X +720 1356(address)N +995(of)X +1096(the)X +1228(buffer,)X +1479(a)X +1548(pointer)X +1808(to)X +1903(the)X +2034(actual)X +2259(buffer,)X +720 1444(and)N +875(a)X +950(pointer)X +1216(to)X +1317(the)X +1454(buffer)X +1690(header)X +1944(for)X +2077(an)X +2191(over\257ow)X +720 1532(page)N +901(if)X +979(it)X +1052(exists,)X +1283(in)X +1374(addition)X +1665(to)X +1756(the)X +1883(LRU)X +2072(links.)X +2296(If)X +2378(the)X +720 1620(buffer)N +950(corresponding)X +1442(to)X +1537(a)X +1606(particular)X +1947(bucket)X +2194(is)X +2280(not)X +2414(in)X +720 1708(memory,)N +1048(its)X +1164(pointer)X +1432(is)X +1526(NULL.)X +1801(In)X +1909(effect,)X +2154(pages)X +2377(are)X +720 1796(linked)N +950(in)X +1042(three)X +1233(ways.)X +1468(Using)X +1689(the)X +1817(buffer)X +2043(headers,)X +2338(they)X +720 1884(are)N +851(linked)X +1083(physically)X +1444(through)X +1725(the)X +1854(LRU)X +2045(links)X +2231(and)X +2378(the)X +720 1972(over\257ow)N +1036(links.)X +1241(Using)X +1462(the)X +1590(pages)X +1803(themselves,)X +2209(they)X +2377(are)X +720 2060(linked)N +943(logically)X +1246(through)X +1518(the)X +1639(over\257ow)X +1946(addresses)X +2276(on)X +2378(the)X +720 2148(page.)N +948(Since)X +1162(over\257ow)X +1482(pages)X +1700(are)X +1834(accessed)X +2151(only)X +2328(after)X +720 2236(their)N +904(predecessor)X +1321(pages,)X +1560(they)X +1734(are)X +1869(removed)X +2186(from)X +2378(the)X +720 2324(buffer)N +937(pool)X +1099(when)X +1293(their)X +1460(primary)X +1734(is)X +1807(removed.)X +10 f +720 2412 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 Dt +2309 3177 MXY +24 15 Dl +-8 -15 Dl +8 -15 Dl +-24 15 Dl +52 0 Dl +789 3160 MXY +-35 0 Dl +0 -156 Dl +1607 0 Dl +0 173 Dl +789 3091 MXY +-24 -15 Dl +9 15 Dl +-9 15 Dl +24 -15 Dl +-69 0 Dl +2309 3125 MXY +104 0 Dl +0 -155 Dl +-1693 0 Dl +0 121 Dl +927 3160 MXY +24 15 Dl +-9 -15 Dl +9 -15 Dl +-24 15 Dl +553 0 Dl +1618 3177 MXY +8 27 Dl +4 -17 Dl +16 -6 Dl +-28 -4 Dl +138 121 Dl +1895 3315 MXY +28 3 Dl +-15 -9 Dl +1 -18 Dl +-14 24 Dl +276 -138 Dl +3108 MY +-28 -3 Dl +15 10 Dl +-1 17 Dl +14 -24 Dl +-276 138 Dl +1756 3229 MXY +-8 -27 Dl +-3 17 Dl +-16 6 Dl +27 4 Dl +-138 -121 Dl +1480 MX +-24 -15 Dl +9 15 Dl +-9 15 Dl +24 -15 Dl +-553 0 Dl +3 f +5 s +1083 3073(LRU)N +1178(chain)X +4 Ds +1402 3851 MXY + 1402 3851 lineto + 1471 3851 lineto + 1471 3920 lineto + 1402 3920 lineto + 1402 3851 lineto +closepath 19 1402 3851 1471 3920 Dp +1445 3747(Over\257ow)N +1613(Address)X +1549 3609 MXY +0 69 Dl +1756 MX +-23 -15 Dl +8 15 Dl +-8 15 Dl +23 -15 Dl +-207 0 Dl +-1 Ds +3 Dt +1756 3419 MXY +-6 -28 Dl +-4 17 Dl +-17 5 Dl +27 6 Dl +-138 -138 Dl +2240 3471 MXY +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +1826 3609 MXY +15 -24 Dl +-15 9 Dl +-16 -9 Dl +16 24 Dl +0 -138 Dl +1549 MX +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +858 3471 MXY +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +2240 3056 MXY +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +1549 3056 MXY +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +858 3056 MXY +15 -24 Dl +-15 9 Dl +-15 -9 Dl +15 24 Dl +0 -138 Dl +1 Dt +2171 3471 MXY + 2171 3471 lineto + 2448 3471 lineto + 2448 3609 lineto + 2171 3609 lineto + 2171 3471 lineto +closepath 19 2171 3471 2448 3609 Dp +1756 3609 MXY + 1756 3609 lineto + 2033 3609 lineto + 2033 3747 lineto + 1756 3747 lineto + 1756 3609 lineto +closepath 3 1756 3609 2033 3747 Dp +1480 3471 MXY + 1480 3471 lineto + 1756 3471 lineto + 1756 3609 lineto + 1480 3609 lineto + 1480 3471 lineto +closepath 19 1480 3471 1756 3609 Dp +789 MX + 789 3471 lineto + 1065 3471 lineto + 1065 3609 lineto + 789 3609 lineto + 789 3471 lineto +closepath 19 789 3471 1065 3609 Dp +962 3903(Buffer)N +1083(Header)X +849 3851 MXY + 849 3851 lineto + 918 3851 lineto + 918 3920 lineto + 849 3920 lineto + 849 3851 lineto +closepath 14 849 3851 918 3920 Dp +1756 3194 MXY + 1756 3194 lineto + 1895 3194 lineto + 1895 3471 lineto + 1756 3471 lineto + 1756 3194 lineto +closepath 14 1756 3194 1895 3471 Dp +2171 3056 MXY + 2171 3056 lineto + 2309 3056 lineto + 2309 3333 lineto + 2171 3333 lineto + 2171 3056 lineto +closepath 14 2171 3056 2309 3333 Dp +1480 MX + 1480 3056 lineto + 1618 3056 lineto + 1618 3333 lineto + 1480 3333 lineto + 1480 3056 lineto +closepath 14 1480 3056 1618 3333 Dp +789 MX + 789 3056 lineto + 927 3056 lineto + 927 3333 lineto + 789 3333 lineto + 789 3056 lineto +closepath 14 789 3056 927 3333 Dp +2780 MY +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +927 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +1065 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +1203 MX +0 138 Dl +139 0 Dl +0 -138 Dl +-139 0 Dl +1342 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +1480 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +1618 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +1756 MX +0 138 Dl +139 0 Dl +0 -138 Dl +-139 0 Dl +1895 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +2033 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +2171 MX +0 138 Dl +138 0 Dl +0 -138 Dl +-138 0 Dl +2309 MX +0 138 Dl +139 0 Dl +0 -138 Dl +-139 0 Dl +13 s +1048 2720(In)N +1173(Memory)X +1580(Bucket)X +1918(Array)X +867 3584(B0)N +1558(B5)X +2223(B10)X +1788 3722(O1/1)N +5 s +1515 3903(Primay)N +1651(Buffer)X +4 Ds +1990 3851 MXY + 1990 3851 lineto + 2059 3851 lineto + 2059 3920 lineto + 1990 3920 lineto + 1990 3851 lineto +closepath 3 1990 3851 2059 3920 Dp +2102 3903(Over\257ow)N +2270(Buffer)X +3 Dt +-1 Ds +8 s +720 4184(Figure)N +922(4:)X +1 f +996(Three)X +1164(primary)X +1386(pages)X +1551(\(B0,)X +1683(B5,)X +1794(B10\))X +1942(are)X +2039(accessed)X +2281(directly)X +720 4272(from)N +862(the)X +958(bucket)X +1146(array.)X +1326(The)X +1443(one)X +1553(over\257ow)X +1798(page)X +1935(\(O1/1\))X +2122(is)X +2182(linked)X +2359(phy-)X +720 4360(sically)N +915(from)X +1067(its)X +1155(primary)X +1384(page's)X +1577(buffer)X +1759(header)X +1955(as)X +2035(well)X +2172(as)X +2252(logically)X +720 4448(from)N +860(its)X +937(predecessor)X +1253(page)X +1389(buffer)X +1560(\(B5\).)X +10 s +10 f +720 4624 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +1191 4954(Table)N +1406(Parameterization)X +1 f +892 5086(When)N +1107(a)X +1166(hash)X +1336(table)X +1515(is)X +1590(created,)X +1865(the)X +1985(bucket)X +2221(size,)X +2388(\256ll)X +720 5174(factor,)N +953(initial)X +1164(number)X +1434(of)X +1526(elements,)X +1856(number)X +2125(of)X +2216(bytes)X +2409(of)X +720 5262(main)N +919(memory)X +1225(used)X +1411(for)X +1543(caching,)X +1851(and)X +2005(a)X +2079(user-de\256ned)X +720 5350(hash)N +892(function)X +1184(may)X +1347(be)X +1448(speci\256ed.)X +1797(The)X +1946(bucket)X +2184(size)X +2333(\(and)X +720 5438(page)N +906(size)X +1064(for)X +1191(over\257ow)X +1509(pages\))X +1752(defaults)X +2039(to)X +2134(256)X +2287(bytes.)X +720 5526(For)N +858(tables)X +1072(with)X +1241(large)X +1429(data)X +1590(items,)X +1810(it)X +1881(may)X +2046(be)X +2149(preferable)X +720 5614(to)N +803(increase)X +1088(the)X +1207(page)X +1380(size,)X +1545(and,)X +1701(conversely,)X +2089(applications)X +720 5702(storing)N +1002(small)X +1235(items)X +1467(exclusively)X +1891(in)X +2012(memory)X +2338(may)X +2706 538(bene\256t)N +2966(from)X +3164(a)X +3242(smaller)X +3520(bucket)X +3776(size.)X +3983(A)X +4082(bucket)X +4337(size)X +2706 626(smaller)N +2962(than)X +3120(64)X +3220(bytes)X +3409(is)X +3482(not)X +3604(recommended.)X +2878 740(The)N +3031(\256ll)X +3147(factor)X +3363(indicates)X +3676(a)X +3740(desired)X +4000(density)X +4258(within)X +2706 828(the)N +2833(hash)X +3009(table.)X +3234(It)X +3312(is)X +3394(an)X +3499(approximation)X +3995(of)X +4091(the)X +4217(number)X +2706 916(of)N +2815(keys)X +3004(allowed)X +3300(to)X +3404(accumulate)X +3811(in)X +3914(any)X +4071(one)X +4228(bucket,)X +2706 1004(determining)N +3119(when)X +3319(the)X +3442(hash)X +3614(table)X +3795(grows.)X +4056(Its)X +4161(default)X +4409(is)X +2706 1092(eight.)N +2953(If)X +3054(the)X +3199(user)X +3380(knows)X +3636(the)X +3781(average)X +4079(size)X +4251(of)X +4364(the)X +2706 1180(key/data)N +3008(pairs)X +3194(being)X +3402(stored)X +3627(in)X +3718(the)X +3845(table,)X +4050(near)X +4218(optimal)X +2706 1268(bucket)N +2943(sizes)X +3122(and)X +3261(\256ll)X +3372(factors)X +3614(may)X +3775(be)X +3874(selected)X +4155(by)X +4257(apply-)X +2706 1356(ing)N +2828(the)X +2946(equation:)X +0 f +8 s +2706 1655(\(1\))N +2994 -0.3938(\(\(average_pair_length)AX +3830(+)X +3906(4\))X +4020(*)X +3032 1743(ffactor\))N +3374(>=)X +3488(bsize)X +1 f +10 s +2706 2042(For)N +2859(highly)X +3104(time)X +3287(critical)X +3551(applications,)X +3999(experimenting)X +2706 2130(with)N +2919(different)X +3266(bucket)X +3550(sizes)X +3776(and)X +3962(\256ll)X +4120(factors)X +4409(is)X +2706 2218(encouraged.)N +2878 2332(Figures)N +3144(5a,b,)X +3326(and)X +3468(c)X +3530(illustrate)X +3836(the)X +3960(effects)X +4200(of)X +4292(vary-)X +2706 2420(ing)N +2841(page)X +3026(sizes)X +3215(and)X +3363(\256ll)X +3483(factors)X +3734(for)X +3860(the)X +3990(same)X +4187(data)X +4353(set.)X +2706 2508(The)N +2864(data)X +3031(set)X +3152(consisted)X +3482(of)X +3581(24474)X +3813(keys)X +3992(taken)X +4198(from)X +4386(an)X +2706 2596(online)N +2931(dictionary.)X +3301(The)X +3451(data)X +3609(value)X +3807(for)X +3925(each)X +4097(key)X +4237(was)X +4386(an)X +2706 2684(ASCII)N +2938(string)X +3143(for)X +3260(an)X +3359(integer)X +3605(from)X +3784(1)X +3847(to)X +3931(24474)X +4153(inclusive.)X +2706 2772(The)N +2867(test)X +3013(run)X +3155(consisted)X +3488(of)X +3590(creating)X +3884(a)X +3955(new)X +4124(hash)X +4306(table)X +2706 2860(\(where)N +2966(the)X +3100(ultimate)X +3398(size)X +3559(of)X +3662(the)X +3796(table)X +3987(was)X +4147(known)X +4400(in)X +2706 2948(advance\),)N +3054(entering)X +3354(each)X +3539(key/data)X +3848(pair)X +4010(into)X +4171(the)X +4306(table)X +2706 3036(and)N +2849(then)X +3014(retrieving)X +3353(each)X +3528(key/data)X +3827(pair)X +3979(from)X +4162(the)X +4286(table.)X +2706 3124(Each)N +2898(of)X +2996(the)X +3125(graphs)X +3369(shows)X +3599(the)X +3727(timings)X +3996(resulting)X +4306(from)X +2706 3212(varying)N +2973(the)X +3093(pagesize)X +3392(from)X +3570(128)X +3712(bytes)X +3903(to)X +3986(1M)X +4118(and)X +4255(the)X +4374(\256ll)X +2706 3300(factor)N +2929(from)X +3120(1)X +3195(to)X +3292(128.)X +3486(For)X +3631(each)X +3813(run,)X +3974(the)X +4106(buffer)X +4337(size)X +2706 3388(was)N +2874(set)X +3006(at)X +3106(1M.)X +3299(The)X +3466(tests)X +3650(were)X +3849(all)X +3971(run)X +4120(on)X +4242(an)X +4360(HP)X +2706 3476(9000/370)N +3077(\(33.3)X +3312(Mhz)X +3527(MC68030\),)X +3966(with)X +4176(16M)X +4395(of)X +2706 3564(memory,)N +3042(64K)X +3228(physically)X +3605(addressed)X +3970(cache,)X +4222(and)X +4386(an)X +2706 3652(HP7959S)N +3055(disk)X +3231(drive,)X +3459(running)X +3751(4.3BSD-Reno)X +4244(single-)X +2706 3740(user.)N +2878 3854(Both)N +3066(system)X +3321(time)X +3496(\(Figure)X +3764(5a\))X +3899(and)X +4047(elapsed)X +4320(time)X +2706 3942(\(Figure)N +2966(5b\))X +3097(show)X +3290(that)X +3434(for)X +3552(all)X +3655(bucket)X +3892(sizes,)X +4091(the)X +4212(greatest)X +2706 4030(performance)N +3137(gains)X +3329(are)X +3451(made)X +3648(by)X +3751(increasing)X +4104(the)X +4225(\256ll)X +4336(fac-)X +2706 4118(tor)N +2822(until)X +2995(equation)X +3298(1)X +3365(is)X +3445(satis\256ed.)X +3774(The)X +3925(user)X +4085(time)X +4253(shown)X +2706 4206(in)N +2791(Figure)X +3023(5c)X +3122(gives)X +3314(a)X +3373(more)X +3561(detailed)X +3838(picture)X +4083(of)X +4172(how)X +4332(per-)X +2706 4294(formance)N +3054(varies.)X +3330(The)X +3499(smaller)X +3778(bucket)X +4035(sizes)X +4234(require)X +2706 4382(fewer)N +2921(keys)X +3099(per)X +3233(page)X +3416(to)X +3509(satisfy)X +3749(equation)X +4056(1)X +4127(and)X +4274(there-)X +2706 4470(fore)N +2860(incur)X +3049(fewer)X +3257(collisions.)X +3607(However,)X +3946(when)X +4144(the)X +4265(buffer)X +2706 4558(pool)N +2884(size)X +3045(is)X +3134(\256xed,)X +3349(smaller)X +3620(pages)X +3838(imply)X +4059(more)X +4259(pages.)X +2706 4646(An)N +2830(increased)X +3160(number)X +3430(of)X +3522(pages)X +3730(means)X +3960(more)X +2 f +4150(malloc\(3\))X +1 f +2706 4734(calls)N +2879(and)X +3021(more)X +3212(overhead)X +3533(in)X +3621(the)X +3745(hash)X +3918(package's)X +4265(buffer)X +2706 4822(manager)N +3003(to)X +3085(manage)X +3355(the)X +3473(additional)X +3813(pages.)X +2878 4936(The)N +3028(tradeoff)X +3308(works)X +3529(out)X +3655(most)X +3834(favorably)X +4166(when)X +4364(the)X +2706 5024(page)N +2886(size)X +3039(is)X +3120(256)X +3268(and)X +3412(the)X +3538(\256ll)X +3654(factor)X +3870(is)X +3950(8.)X +4057(Similar)X +4319(con-)X +2706 5112(clusions)N +3009(were)X +3207(obtained)X +3524(if)X +3614(the)X +3753(test)X +3905(was)X +4071(run)X +4218(without)X +2706 5200(knowing)N +3007(the)X +3126(\256nal)X +3289(table)X +3466(size)X +3612(in)X +3695(advance.)X +4020(If)X +4095(the)X +4214(\256le)X +4337(was)X +2706 5288(closed)N +2942(and)X +3088(written)X +3345(to)X +3437(disk,)X +3620(the)X +3748(conclusions)X +4156(were)X +4343(still)X +2706 5376(the)N +2832(same.)X +3065(However,)X +3408(rereading)X +3740(the)X +3865(\256le)X +3994(from)X +4177(disk)X +4337(was)X +2706 5464(slightly)N +2983(faster)X +3199(if)X +3285(a)X +3358(larger)X +3583(bucket)X +3834(size)X +3996(and)X +4149(\256ll)X +4274(factor)X +2706 5552(were)N +2898(used)X +3079(\(1K)X +3238(bucket)X +3486(size)X +3645(and)X +3795(32)X +3909(\256ll)X +4031(factor\).)X +4320(This)X +2706 5640(follows)N +2987(intuitively)X +3356(from)X +3553(the)X +3691(improved)X +4038(ef\256ciency)X +4395(of)X +3 f +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4424(7)X + +8 p +%%Page: 8 8 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +1 f +432 538(performing)N +830(1K)X +965(reads)X +1172(from)X +1365(the)X +1500(disk)X +1670(rather)X +1894(than)X +2068(256)X +432 626(byte)N +609(reads.)X +857(In)X +962(general,)X +1257(performance)X +1702(for)X +1834(disk)X +2005(based)X +432 714(tables)N +639(is)X +712(best)X +861(when)X +1055(the)X +1173(page)X +1345(size)X +1490(is)X +1563(approximately)X +2046(1K.)X +10 f +432 802 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +619 2380 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +629 2437 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +648 2504 MXY +-12 25 Dl +24 0 Dl +-12 -25 Dl +686 2515 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +762 2516 MXY +-12 24 Dl +25 0 Dl +-13 -24 Dl +916 2515 MXY +-13 24 Dl +25 0 Dl +-12 -24 Dl +1222 2516 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +1834 2515 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +1 Dt +619 2392 MXY +10 57 Dl +19 67 Dl +38 11 Dl +76 1 Dl +154 -1 Dl +306 1 Dl +612 -1 Dl +8 s +1 f +1628 2522(128)N +3 Dt +607 2245 MXY +24 Dc +617 2375 MXY +23 Dc +635 2442 MXY +24 Dc +674 2525 MXY +23 Dc +750 2529 MXY +24 Dc +904 2527 MXY +23 Dc +1210 MX +23 Dc +1822 2528 MXY +23 Dc +20 Ds +1 Dt +619 2245 MXY +10 130 Dl +19 67 Dl +38 83 Dl +76 4 Dl +154 -2 Dl +306 0 Dl +612 1 Dl +678 2482(256)N +-1 Ds +3 Dt +619 2127 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +629 2191 MXY +0 25 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +648 2334 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +686 2409 MXY +0 25 Dl +0 -13 Dl +12 0 Dl +-24 0 Dl +762 2516 MXY +0 25 Dl +0 -12 Dl +13 0 Dl +-25 0 Dl +916 2516 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-25 0 Dl +1222 2515 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +1834 2515 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +5 Dt +619 2139 MXY +10 65 Dl +19 142 Dl +38 75 Dl +76 108 Dl +154 -1 Dl +306 -1 Dl +612 0 Dl +694 2401(512)N +3 Dt +631 2064 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +641 2077 MXY +-24 25 Dl +12 -12 Dl +-12 -13 Dl +24 25 Dl +660 2132 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +698 2292 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +775 2382 MXY +-25 24 Dl +12 -12 Dl +-12 -12 Dl +25 24 Dl +928 2516 MXY +-25 24 Dl +13 -12 Dl +-13 -12 Dl +25 24 Dl +1234 2516 MXY +-24 25 Dl +12 -12 Dl +-12 -13 Dl +24 25 Dl +1846 2516 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +16 Ds +1 Dt +619 2076 MXY +10 14 Dl +19 54 Dl +38 160 Dl +76 90 Dl +154 134 Dl +306 1 Dl +612 -1 Dl +694 2257(1024)N +-1 Ds +3 Dt +619 1877 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +629 1855 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +648 1838 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +686 1860 MXY +12 -25 Dl +-24 0 Dl +12 25 Dl +762 1923 MXY +13 -24 Dl +-25 0 Dl +12 24 Dl +916 2087 MXY +12 -24 Dl +-25 0 Dl +13 24 Dl +1222 2256 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +1834 2541 MXY +12 -25 Dl +-24 0 Dl +12 25 Dl +619 1865 MXY +10 -22 Dl +19 -17 Dl +38 21 Dl +76 64 Dl +154 164 Dl +306 169 Dl +612 285 Dl +1645 2427(4096)N +619 1243 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +629 1196 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +648 1146 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +686 1174 MXY +0 25 Dl +0 -13 Dl +12 0 Dl +-24 0 Dl +762 1249 MXY +0 24 Dl +0 -12 Dl +13 0 Dl +-25 0 Dl +916 1371 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-25 0 Dl +1222 1680 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +1834 1999 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +619 1255 MXY +10 -47 Dl +19 -50 Dl +38 28 Dl +76 75 Dl +154 122 Dl +306 309 Dl +612 319 Dl +1741 1934(8192)N +5 Dt +609 2531 MXY +1225 0 Dl +609 MX +0 -1553 Dl +2531 MY +0 16 Dl +4 Ds +1 Dt +2531 MY +0 -1553 Dl +593 2625(0)N +-1 Ds +5 Dt +916 2531 MXY +0 16 Dl +4 Ds +1 Dt +2531 MY +0 -1553 Dl +884 2625(32)N +-1 Ds +5 Dt +1222 2531 MXY +0 16 Dl +4 Ds +1 Dt +2531 MY +0 -1553 Dl +1190 2625(64)N +-1 Ds +5 Dt +1528 2531 MXY +0 16 Dl +4 Ds +1 Dt +2531 MY +0 -1553 Dl +1496 2625(96)N +-1 Ds +5 Dt +1834 2531 MXY +0 16 Dl +4 Ds +1 Dt +2531 MY +0 -1553 Dl +1786 2625(128)N +-1 Ds +5 Dt +609 2531 MXY +-16 0 Dl +4 Ds +1 Dt +609 MX +1225 0 Dl +545 2558(0)N +-1 Ds +5 Dt +609 2013 MXY +-16 0 Dl +4 Ds +1 Dt +609 MX +1225 0 Dl +481 2040(100)N +-1 Ds +5 Dt +609 1496 MXY +-16 0 Dl +4 Ds +1 Dt +609 MX +1225 0 Dl +481 1523(200)N +-1 Ds +5 Dt +609 978 MXY +-16 0 Dl +4 Ds +1 Dt +609 MX +1225 0 Dl +481 1005(300)N +1088 2724(Fill)N +1194(Factor)X +422 1611(S)N +426 1667(e)N +426 1724(c)N +424 1780(o)N +424 1837(n)N +424 1893(d)N +428 1949(s)N +3 Dt +-1 Ds +3 f +432 2882(Figure)N +636(5a:)X +1 f +744(System)X +956(Time)X +1113(for)X +1209(dictionary)X +1490(data)X +1618(set)X +1711(with)X +1847(1M)X +1958(of)X +2033(buffer)X +432 2970(space)N +594(and)X +707(varying)X +923(bucket)X +1114(sizes)X +1259(and)X +1372(\256ll)X +1465(factors.)X +1675(Each)X +1823(line)X +1940(is)X +2004(labeled)X +432 3058(with)N +562(its)X +639(bucket)X +825(size.)X +10 s +10 f +432 3234 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +8 s +1 f +428 4381(s)N +424 4325(d)N +424 4269(n)N +424 4212(o)N +426 4156(c)N +426 4099(e)N +422 4043(S)N +1116 5156(Fill)N +1222(Factor)X +506 3437(3200)N +4 Ds +1 Dt +666 3410 MXY +1168 0 Dl +-1 Ds +5 Dt +666 MX +-16 0 Dl +506 3825(2400)N +4 Ds +1 Dt +666 3799 MXY +1168 0 Dl +-1 Ds +5 Dt +666 MX +-16 0 Dl +506 4214(1600)N +4 Ds +1 Dt +666 4186 MXY +1168 0 Dl +-1 Ds +5 Dt +666 MX +-16 0 Dl +538 4602(800)N +4 Ds +1 Dt +666 4575 MXY +1168 0 Dl +-1 Ds +5 Dt +666 MX +-16 0 Dl +602 4990(0)N +4 Ds +1 Dt +666 4963 MXY +1168 0 Dl +-1 Ds +5 Dt +666 MX +-16 0 Dl +1786 5057(128)N +4 Ds +1 Dt +1834 4963 MXY +0 -1553 Dl +-1 Ds +5 Dt +4963 MY +0 16 Dl +1510 5057(96)N +4 Ds +1 Dt +1542 4963 MXY +0 -1553 Dl +-1 Ds +5 Dt +4963 MY +0 16 Dl +1218 5057(64)N +4 Ds +1 Dt +1250 4963 MXY +0 -1553 Dl +-1 Ds +5 Dt +4963 MY +0 16 Dl +926 5057(32)N +4 Ds +1 Dt +958 4963 MXY +0 -1553 Dl +-1 Ds +5 Dt +4963 MY +0 16 Dl +650 5057(0)N +4 Ds +1 Dt +666 4963 MXY +0 -1553 Dl +-1 Ds +5 Dt +4963 MY +0 16 Dl +4963 MY +0 -1553 Dl +4963 MY +1168 0 Dl +1741 4752(8192)N +3 Dt +675 3732 MXY +9 -172 Dl +18 -118 Dl +37 128 Dl +73 -121 Dl +146 623 Dl +292 497 Dl +584 245 Dl +4802 MY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +1250 4557 MXY +0 25 Dl +0 -13 Dl +12 0 Dl +-24 0 Dl +958 4060 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +812 3437 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +739 3558 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +702 3430 MXY +0 25 Dl +0 -13 Dl +13 0 Dl +-25 0 Dl +684 3548 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +675 3720 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +1637 4912(4096)N +675 4307 MXY +9 -58 Dl +18 30 Dl +37 89 Dl +73 144 Dl +146 235 Dl +292 122 Dl +584 89 Dl +4970 MY +12 -24 Dl +-24 0 Dl +12 24 Dl +1250 4881 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +958 4759 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +812 4524 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +739 4380 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +702 4291 MXY +13 -24 Dl +-25 0 Dl +12 24 Dl +684 4261 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +675 4319 MXY +12 -24 Dl +-24 0 Dl +12 24 Dl +734 4662(1024)N +16 Ds +1 Dt +675 4352 MXY +9 60 Dl +18 134 Dl +37 266 Dl +73 117 Dl +146 30 Dl +292 0 Dl +584 -1 Dl +-1 Ds +3 Dt +1846 4946 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +1262 4946 MXY +-24 25 Dl +12 -12 Dl +-12 -13 Dl +24 25 Dl +970 4947 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +824 4917 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +751 4800 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +715 4534 MXY +-25 25 Dl +12 -13 Dl +-12 -12 Dl +25 25 Dl +696 4400 MXY +-24 24 Dl +12 -12 Dl +-12 -12 Dl +24 24 Dl +687 4339 MXY +-24 25 Dl +12 -12 Dl +-12 -13 Dl +24 25 Dl +718 4792(512)N +5 Dt +675 4422 MXY +9 137 Dl +18 278 Dl +37 105 Dl +73 18 Dl +146 -1 Dl +292 0 Dl +584 -1 Dl +3 Dt +4946 MY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +1250 4946 MXY +0 25 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +958 4947 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +812 4948 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +739 4930 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +702 4824 MXY +0 25 Dl +0 -12 Dl +13 0 Dl +-25 0 Dl +684 4547 MXY +0 24 Dl +0 -12 Dl +12 0 Dl +-24 0 Dl +675 4410 MXY +0 25 Dl +0 -13 Dl +12 0 Dl +-24 0 Dl +750 4921(256)N +20 Ds +1 Dt +675 4597 MXY +9 246 Dl +18 106 Dl +37 10 Dl +73 0 Dl +146 0 Dl +292 0 Dl +584 -1 Dl +-1 Ds +3 Dt +1822 MX +23 Dc +1238 4959 MXY +23 Dc +946 MX +23 Dc +800 MX +23 Dc +727 MX +23 Dc +691 4949 MXY +23 Dc +672 4843 MXY +24 Dc +663 4597 MXY +24 Dc +1395 4961(128)N +1 Dt +675 4855 MXY +9 93 Dl +18 10 Dl +37 1 Dl +73 0 Dl +146 -1 Dl +292 0 Dl +584 0 Dl +3 Dt +4946 MY +-12 24 Dl +24 0 Dl +-12 -24 Dl +1250 MX +-12 24 Dl +24 0 Dl +-12 -24 Dl +958 MX +-12 24 Dl +24 0 Dl +-12 -24 Dl +812 MX +-12 25 Dl +24 0 Dl +-12 -25 Dl +739 4947 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +702 4946 MXY +-12 24 Dl +25 0 Dl +-13 -24 Dl +684 4936 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +675 4843 MXY +-12 24 Dl +24 0 Dl +-12 -24 Dl +3 Dt +-1 Ds +3 f +432 5314(Figure)N +634(5b:)X +1 f +744(Elapsed)X +967(Time)X +1123(for)X +1218(dictionary)X +1498(data)X +1625(set)X +1717(with)X +1851(1M)X +1960(of)X +2033(buffer)X +432 5402(space)N +593(and)X +705(varying)X +920(bucket)X +1110(sizes)X +1254(and)X +1366(\256ll)X +1457(factors.)X +1681(Each)X +1827(line)X +1942(is)X +2004(labeled)X +432 5490(with)N +562(its)X +639(bucket)X +825(size.)X +10 s +2590 538(If)N +2677(an)X +2785(approximation)X +3284(of)X +3383(the)X +3513(number)X +3790(of)X +3889(elements)X +2418 626(ultimately)N +2773(to)X +2866(be)X +2973(stored)X +3200(in)X +3293(the)X +3422(hash)X +3599(table)X +3785(is)X +3868(known)X +4116(at)X +2418 714(the)N +2564(time)X +2754(of)X +2869(creation,)X +3196(the)X +3342(hash)X +3536(package)X +3847(takes)X +4059(this)X +2418 802(number)N +2688(as)X +2779(a)X +2839(parameter)X +3185(and)X +3325(uses)X +3487(it)X +3555(to)X +3641(hash)X +3812(entries)X +4050(into)X +2418 890(the)N +2541(full)X +2677(sized)X +2867(table)X +3048(rather)X +3261(than)X +3424(growing)X +3716(the)X +3838(table)X +4018(from)X +2418 978(a)N +2477(single)X +2691(bucket.)X +2968(If)X +3044(this)X +3181(number)X +3448(is)X +3523(not)X +3647(known,)X +3907(the)X +4027(hash)X +2418 1066(table)N +2632(starts)X +2859(with)X +3059(a)X +3153(single)X +3402(bucket)X +3674(and)X +3848(gracefully)X +2418 1154(expands)N +2707(as)X +2800(elements)X +3111(are)X +3236(added,)X +3474(although)X +3780(a)X +3842(slight)X +4044(per-)X +2418 1242(formance)N +2747(degradation)X +3151(may)X +3313(be)X +3413(noticed.)X +3713(Figure)X +3946(6)X +4010(illus-)X +2418 1330(trates)N +2625(the)X +2756(difference)X +3116(in)X +3211(performance)X +3651(between)X +3952(storing)X +2418 1418(keys)N +2588(in)X +2673(a)X +2732(\256le)X +2857(when)X +3054(the)X +3174(ultimate)X +3458(size)X +3605(is)X +3680(known)X +3920(\(the)X +4067(left)X +2418 1506(bars)N +2581(in)X +2672(each)X +2849(set\),)X +3014(compared)X +3360(to)X +3450(building)X +3744(the)X +3870(\256le)X +4000(when)X +2418 1594(the)N +2550(ultimate)X +2846(size)X +3005(is)X +3091(unknown)X +3422(\(the)X +3580(right)X +3764(bars)X +3931(in)X +4026(each)X +2418 1682(set\).)N +2609(Once)X +2814(the)X +2947(\256ll)X +3069(factor)X +3291(is)X +3378(suf\256ciently)X +3772(high)X +3948(for)X +4076(the)X +2418 1770(page)N +2596(size)X +2747(\(8\),)X +2887(growing)X +3180(the)X +3304(table)X +3486(dynamically)X +3908(does)X +4081(lit-)X +2418 1858(tle)N +2518(to)X +2600(degrade)X +2875(performance.)X +10 f +2418 1946 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +9 s +1 f +2413 3238(s)N +2409 3173(d)N +2409 3108(n)N +2409 3043(o)N +2411 2979(c)N +2411 2914(e)N +2407 2849(S)N +3143 4129(Fill)N +3261(Factor)X +2448 2152(15)N +4 Ds +1 Dt +2557 2122 MXY +1473 0 Dl +-1 Ds +5 Dt +2557 MX +-19 0 Dl +2448 2747(10)N +4 Ds +1 Dt +2557 2717 MXY +1473 0 Dl +-1 Ds +5 Dt +2557 MX +-19 0 Dl +2484 3343(5)N +4 Ds +1 Dt +2557 3313 MXY +1473 0 Dl +-1 Ds +5 Dt +2557 MX +-19 0 Dl +2484 3938(0)N +4 Ds +1 Dt +2557 3908 MXY +1473 0 Dl +-1 Ds +5 Dt +2557 MX +-19 0 Dl +3976 4015(128)N +4 Ds +1 Dt +4030 3908 MXY +0 -1786 Dl +-1 Ds +5 Dt +3908 MY +0 19 Dl +3626 4015(96)N +4 Ds +1 Dt +3662 3908 MXY +0 -1786 Dl +-1 Ds +5 Dt +3908 MY +0 19 Dl +3258 4015(64)N +4 Ds +1 Dt +3294 3908 MXY +0 -1786 Dl +-1 Ds +5 Dt +3908 MY +0 19 Dl +2889 4015(32)N +4 Ds +1 Dt +2925 3908 MXY +0 -1786 Dl +-1 Ds +5 Dt +3908 MY +0 19 Dl +2539 4015(0)N +4 Ds +1 Dt +2557 3908 MXY +0 -1786 Dl +-1 Ds +5 Dt +3908 MY +0 19 Dl +3908 MY +0 -1786 Dl +3908 MY +1473 0 Dl +4053 2378(8192)N +3 Dt +2569 2277 MXY +11 0 Dl +23 48 Dl +46 -167 Dl +92 35 Dl +184 12 Dl +369 143 Dl +736 0 Dl +2334 MY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +3294 2334 MXY +0 28 Dl +0 -14 Dl +13 0 Dl +-27 0 Dl +2925 2192 MXY +0 27 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2741 2180 MXY +0 27 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2649 2144 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2603 2311 MXY +0 27 Dl +0 -13 Dl +14 0 Dl +-28 0 Dl +2580 2263 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2569 2263 MXY +0 28 Dl +0 -14 Dl +13 0 Dl +-27 0 Dl +4053 2591(4096)N +2569 2348 MXY +11 -11 Dl +23 -96 Dl +46 71 Dl +92 72 Dl +184 226 Dl +369 48 Dl +736 -60 Dl +2612 MY +14 -28 Dl +-28 0 Dl +14 28 Dl +3294 2672 MXY +13 -28 Dl +-27 0 Dl +14 28 Dl +2925 2624 MXY +14 -28 Dl +-28 0 Dl +14 28 Dl +2741 2398 MXY +14 -28 Dl +-28 0 Dl +14 28 Dl +2649 2326 MXY +14 -27 Dl +-28 0 Dl +14 27 Dl +2603 2255 MXY +14 -28 Dl +-28 0 Dl +14 28 Dl +2580 2350 MXY +14 -27 Dl +-28 0 Dl +14 27 Dl +2569 2362 MXY +13 -28 Dl +-27 0 Dl +14 28 Dl +4053 2681(1024)N +16 Ds +1 Dt +2569 2300 MXY +11 48 Dl +23 96 Dl +46 95 Dl +92 274 Dl +184 202 Dl +369 -155 Dl +736 -190 Dl +-1 Ds +3 Dt +4044 2656 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +3307 2846 MXY +-27 28 Dl +14 -14 Dl +-14 -14 Dl +27 28 Dl +2939 3001 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +2755 2799 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +2663 2525 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +2617 2430 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +2594 2334 MXY +-28 28 Dl +14 -14 Dl +-14 -14 Dl +28 28 Dl +2582 2287 MXY +-27 27 Dl +14 -14 Dl +-14 -13 Dl +27 27 Dl +4053 2851(512)N +5 Dt +2569 2372 MXY +11 -24 Dl +23 405 Dl +46 83 Dl +92 227 Dl +184 -72 Dl +369 -119 Dl +736 -107 Dl +3 Dt +2751 MY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +3294 2858 MXY +0 28 Dl +0 -14 Dl +13 0 Dl +-27 0 Dl +2925 2977 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2741 3049 MXY +0 27 Dl +0 -13 Dl +14 0 Dl +-28 0 Dl +2649 2823 MXY +0 27 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2603 2739 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2580 2334 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2569 2358 MXY +0 28 Dl +0 -14 Dl +13 0 Dl +-27 0 Dl +4053 2795(256)N +20 Ds +1 Dt +2569 2456 MXY +11 285 Dl +23 95 Dl +46 251 Dl +92 -60 Dl +184 -84 Dl +369 -107 Dl +736 -71 Dl +-1 Ds +3 Dt +4016 MX +27 Dc +3280 2836 MXY +27 Dc +2912 2943 MXY +27 Dc +2728 3027 MXY +27 Dc +2635 3087 MXY +28 Dc +2589 2836 MXY +28 Dc +2566 2741 MXY +27 Dc +2554 2456 MXY +28 Dc +4053 2741(128)N +1 Dt +2569 2729 MXY +11 203 Dl +23 131 Dl +46 -60 Dl +92 -119 Dl +184 -60 Dl +369 -83 Dl +736 -12 Dl +3 Dt +2716 MY +-14 27 Dl +28 0 Dl +-14 -27 Dl +3294 2727 MXY +-14 28 Dl +27 0 Dl +-13 -28 Dl +2925 2811 MXY +-14 27 Dl +28 0 Dl +-14 -27 Dl +2741 2870 MXY +-14 28 Dl +28 0 Dl +-14 -28 Dl +2649 2989 MXY +-14 28 Dl +28 0 Dl +-14 -28 Dl +2603 3049 MXY +-14 27 Dl +28 0 Dl +-14 -27 Dl +2580 2918 MXY +-14 28 Dl +28 0 Dl +-14 -28 Dl +2569 2716 MXY +-14 27 Dl +27 0 Dl +-13 -27 Dl +3 Dt +-1 Ds +3 f +8 s +2418 4286(Figure)N +2628(5c:)X +1 f +2738(User)X +2887(Time)X +3051(for)X +3154(dictionary)X +3442(data)X +3577(set)X +3677(with)X +3820(1M)X +3938(of)X +4019(buffer)X +2418 4374(space)N +2579(and)X +2691(varying)X +2906(bucket)X +3096(sizes)X +3240(and)X +3352(\256ll)X +3443(factors.)X +3667(Each)X +3813(line)X +3928(is)X +3990(labeled)X +2418 4462(with)N +2548(its)X +2625(bucket)X +2811(size.)X +10 s +10 f +2418 4638 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 f +2590 4840(Since)N +2796(no)X +2904(known)X +3150(hash)X +3325(function)X +3620(performs)X +3938(equally)X +2418 4928(well)N +2589(on)X +2702(all)X +2815(possible)X +3110(data,)X +3297(the)X +3428(user)X +3595(may)X +3766(\256nd)X +3923(that)X +4076(the)X +2418 5016(built-in)N +2678(hash)X +2849(function)X +3140(does)X +3311(poorly)X +3544(on)X +3648(a)X +3708(particular)X +4040(data)X +2418 5104(set.)N +2548(In)X +2636(this)X +2771(case,)X +2950(a)X +3006(hash)X +3173(function,)X +3480(taking)X +3700(two)X +3840(arguments)X +2418 5192(\(a)N +2507(pointer)X +2760(to)X +2848(a)X +2910(byte)X +3074(string)X +3282(and)X +3424(a)X +3486(length\))X +3739(and)X +3880(returning)X +2418 5280(an)N +2517(unsigned)X +2829(long)X +2993(to)X +3077(be)X +3175(used)X +3344(as)X +3433(the)X +3553(hash)X +3722(value,)X +3938(may)X +4098(be)X +2418 5368(speci\256ed)N +2731(at)X +2817(hash)X +2992(table)X +3176(creation)X +3463(time.)X +3673(When)X +3893(an)X +3996(exist-)X +2418 5456(ing)N +2570(hash)X +2767(table)X +2973(is)X +3076(opened)X +3358(and)X +3524(a)X +3609(hash)X +3805(function)X +4121(is)X +2418 5544(speci\256ed,)N +2752(the)X +2879(hash)X +3054(package)X +3346(will)X +3498(try)X +3615(to)X +3705(determine)X +4054(that)X +2418 5632(the)N +2546(hash)X +2723(function)X +3020(supplied)X +3321(is)X +3404(the)X +3532(one)X +3678(with)X +3850(which)X +4076(the)X +2418 5720(table)N +2630(was)X +2811(created.)X +3139(There)X +3382(are)X +3536(a)X +3627(variety)X +3905(of)X +4027(hash)X +3 f +432 5960(8)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +9 p +%%Page: 9 9 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +1 f +720 538(functions)N +1065(provided)X +1397(with)X +1586(the)X +1731(package.)X +2082(The)X +2253(default)X +720 626(function)N +1014(for)X +1135(the)X +1260(package)X +1551(is)X +1631(the)X +1755(one)X +1897(which)X +2119(offered)X +2378(the)X +720 714(best)N +875(performance)X +1308(in)X +1396(terms)X +1600(of)X +1693(cycles)X +1920(executed)X +2232(per)X +2360(call)X +720 802(\(it)N +827(did)X +965(not)X +1103(produce)X +1398(the)X +1531(fewest)X +1776(collisions)X +2117(although)X +2432(it)X +720 890(was)N +866(within)X +1091(a)X +1148(small)X +1341(percentage)X +1710(of)X +1797(the)X +1915(function)X +2202(that)X +2342(pro-)X +720 978(duced)N +947(the)X +1080(fewest)X +1324(collisions\).)X +1731(Again,)X +1981(in)X +2077(time)X +2253(critical)X +720 1066(applications,)N +1152(users)X +1342(are)X +1466(encouraged)X +1862(to)X +1949(experiment)X +2334(with)X +720 1154(a)N +783(variety)X +1032(of)X +1125(hash)X +1298(functions)X +1622(to)X +1710(achieve)X +1982(optimal)X +2252(perfor-)X +720 1242(mance.)N +10 f +720 1330 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +7 s +1038 2925(Full)N +1149(size)X +1251(table)X +1384(\(left\))X +1547 2718(Fill)N +1643(Factor)X +2268 2662(64)N +1964(32)X +1674(16)X +1384(8)X +1093(4)X +4 Ds +1 Dt +900 2280 MXY +1548 0 Dl +900 1879 MXY +1548 0 Dl +900 1506 MXY +1548 0 Dl +1563 2902 MXY +111 0 Dl +-1 Ds +900 MX +110 0 Dl +1425 2828(System)N +983(User)X +1895 2778 MXY + 1895 2778 lineto + 1950 2778 lineto + 1950 2833 lineto + 1895 2833 lineto + 1895 2778 lineto +closepath 21 1895 2778 1950 2833 Dp +1342 MX + 1342 2778 lineto + 1397 2778 lineto + 1397 2833 lineto + 1342 2833 lineto + 1342 2778 lineto +closepath 14 1342 2778 1397 2833 Dp +900 MX + 900 2778 lineto + 955 2778 lineto + 955 2833 lineto + 900 2833 lineto + 900 2778 lineto +closepath 3 900 2778 955 2833 Dp +5 Dt +2283 2211 MXY +96 0 Dl +1992 MX +97 0 Dl +1702 MX +97 0 Dl +1411 2252 MXY +97 0 Dl +4 Ds +1 Dt +2283 2211 MXY + 2283 2211 lineto + 2379 2211 lineto + 2379 2252 lineto + 2283 2252 lineto + 2283 2211 lineto +closepath 14 2283 2211 2379 2252 Dp +1992 MX + 1992 2211 lineto + 2089 2211 lineto + 2089 2252 lineto + 1992 2252 lineto + 1992 2211 lineto +closepath 14 1992 2211 2089 2252 Dp +1702 MX + 1702 2211 lineto + 1799 2211 lineto + 1799 2252 lineto + 1702 2252 lineto + 1702 2211 lineto +closepath 14 1702 2211 1799 2252 Dp +1411 2252 MXY + 1411 2252 lineto + 1508 2252 lineto + 1508 2294 lineto + 1411 2294 lineto + 1411 2252 lineto +closepath 14 1411 2252 1508 2294 Dp +2283 MX + 2283 2252 lineto + 2379 2252 lineto + 2379 2612 lineto + 2283 2612 lineto + 2283 2252 lineto +closepath 3 2283 2252 2379 2612 Dp +1992 MX + 1992 2252 lineto + 2089 2252 lineto + 2089 2612 lineto + 1992 2612 lineto + 1992 2252 lineto +closepath 3 1992 2252 2089 2612 Dp +1702 MX + 1702 2252 lineto + 1799 2252 lineto + 1799 2612 lineto + 1702 2612 lineto + 1702 2252 lineto +closepath 3 1702 2252 1799 2612 Dp +1411 2294 MXY + 1411 2294 lineto + 1508 2294 lineto + 1508 2612 lineto + 1411 2612 lineto + 1411 2294 lineto +closepath 3 1411 2294 1508 2612 Dp +-1 Ds +2158 2238 MXY + 2158 2238 lineto + 2255 2238 lineto + 2255 2252 lineto + 2158 2252 lineto + 2158 2238 lineto +closepath 21 2158 2238 2255 2252 Dp +1868 MX + 1868 2238 lineto + 1965 2238 lineto + 1965 2280 lineto + 1868 2280 lineto + 1868 2238 lineto +closepath 21 1868 2238 1965 2280 Dp +1577 MX + 1577 2238 lineto + 1674 2238 lineto + 1674 2308 lineto + 1577 2308 lineto + 1577 2238 lineto +closepath 21 1577 2238 1674 2308 Dp +1287 2308 MXY + 1287 2308 lineto + 1287 2280 lineto + 1384 2280 lineto + 1384 2308 lineto + 1287 2308 lineto +closepath 21 1287 2280 1384 2308 Dp +2158 2280 MXY + 2158 2280 lineto + 2158 2252 lineto + 2255 2252 lineto + 2255 2280 lineto + 2158 2280 lineto +closepath 14 2158 2252 2255 2280 Dp +1868 2308 MXY + 1868 2308 lineto + 1868 2280 lineto + 1965 2280 lineto + 1965 2308 lineto + 1868 2308 lineto +closepath 14 1868 2280 1965 2308 Dp +1577 2335 MXY + 1577 2335 lineto + 1577 2308 lineto + 1674 2308 lineto + 1674 2335 lineto + 1577 2335 lineto +closepath 14 1577 2308 1674 2335 Dp +1287 2363 MXY + 1287 2363 lineto + 1287 2308 lineto + 1384 2308 lineto + 1384 2363 lineto + 1287 2363 lineto +closepath 14 1287 2308 1384 2363 Dp +2158 2280 MXY + 2158 2280 lineto + 2255 2280 lineto + 2255 2612 lineto + 2158 2612 lineto + 2158 2280 lineto +closepath 3 2158 2280 2255 2612 Dp +1868 2308 MXY + 1868 2308 lineto + 1965 2308 lineto + 1965 2612 lineto + 1868 2612 lineto + 1868 2308 lineto +closepath 3 1868 2308 1965 2612 Dp +1577 2335 MXY + 1577 2335 lineto + 1674 2335 lineto + 1674 2612 lineto + 1577 2612 lineto + 1577 2335 lineto +closepath 3 1577 2335 1674 2612 Dp +1287 2363 MXY + 1287 2363 lineto + 1384 2363 lineto + 1384 2612 lineto + 1287 2612 lineto + 1287 2363 lineto +closepath 3 1287 2363 1384 2612 Dp +4 Ds +1121 2066 MXY + 1121 2066 lineto + 1218 2066 lineto + 1224 2080 lineto + 1127 2080 lineto + 1121 2066 lineto +closepath 21 1121 2066 1224 2080 Dp +2080 MY + 1121 2080 lineto + 1218 2080 lineto + 1218 2273 lineto + 1121 2273 lineto + 1121 2080 lineto +closepath 14 1121 2080 1218 2273 Dp +2273 MY + 1121 2273 lineto + 1218 2273 lineto + 1218 2612 lineto + 1121 2612 lineto + 1121 2273 lineto +closepath 3 1121 2273 1218 2612 Dp +-1 Ds +997 1589 MXY + 997 1589 lineto + 1093 1589 lineto + 1093 1644 lineto + 997 1644 lineto + 997 1589 lineto +closepath 21 997 1589 1093 1644 Dp +1644 MY + 997 1644 lineto + 1093 1644 lineto + 1093 2280 lineto + 997 2280 lineto + 997 1644 lineto +closepath 14 997 1644 1093 2280 Dp +2280 MY + 997 2280 lineto + 1093 2280 lineto + 1093 2612 lineto + 997 2612 lineto + 997 2280 lineto +closepath 3 997 2280 1093 2612 Dp +10 s +719 2093(s)N +712 2037(d)N +712 1982(n)N +714 1927(o)N +716 1872(c)N +716 1816(e)N +712 1761(S)N +804 2286(10)N +804 1899(20)N +804 1540(30)N +3 Dt +900 1506 MXY +0 1106 Dl +1548 0 Dl +7 s +1978 2828(Elapsed)N +1701 2925(Dynamically)N +2018(grown)X +2184(table)X +2317(\(right\))X +3 Dt +-1 Ds +8 s +720 3180(Figure)N +934(6:)X +1 f +1020(The)X +1152(total)X +1299(regions)X +1520(indicate)X +1755(the)X +1865(difference)X +2154(between)X +2398(the)X +720 3268(elapsed)N +931(time)X +1065(and)X +1177(the)X +1275(sum)X +1402(of)X +1475(the)X +1573(system)X +1771(and)X +1883(user)X +2008(time.)X +2173(The)X +2291(left)X +2395(bar)X +720 3356(of)N +798(each)X +939(set)X +1035(depicts)X +1241(the)X +1344(timing)X +1537(of)X +1615(the)X +1718(test)X +1831(run)X +1940(when)X +2102(the)X +2204(number)X +2423(of)X +720 3444(entries)N +910(is)X +973(known)X +1167(in)X +1237(advance.)X +1496(The)X +1614(right)X +1754(bars)X +1879(depict)X +2054(the)X +2151(timing)X +2338(when)X +720 3532(the)N +814(\256le)X +912(is)X +971(grown)X +1150(from)X +1290(a)X +1334(single)X +1503(bucket.)X +10 s +10 f +720 3708 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +1 f +892 3910(Since)N +1131(this)X +1307(hashing)X +1617(package)X +1942(provides)X +2279(buffer)X +720 3998(management,)N +1188(the)X +1323(amount)X +1600(of)X +1704(space)X +1920(allocated)X +2247(for)X +2378(the)X +720 4086(buffer)N +948(pool)X +1121(may)X +1290(be)X +1397(speci\256ed)X +1713(by)X +1824(the)X +1953(user.)X +2157(Using)X +2378(the)X +720 4174(same)N +910(data)X +1069(set)X +1183(and)X +1324(test)X +1459(procedure)X +1805(as)X +1896(used)X +2067(to)X +2153(derive)X +2378(the)X +720 4262(graphs)N +962(in)X +1052(Figures)X +1320(5a-c,)X +1507(Figure)X +1744(7)X +1812(shows)X +2039(the)X +2164(impact)X +2409(of)X +720 4350(varying)N +997(the)X +1126(size)X +1282(of)X +1380(the)X +1509(buffer)X +1737(pool.)X +1950(The)X +2106(bucket)X +2351(size)X +720 4438(was)N +873(set)X +989(to)X +1078(256)X +1225(bytes)X +1421(and)X +1564(the)X +1689(\256ll)X +1804(factor)X +2019(was)X +2171(set)X +2287(to)X +2376(16.)X +720 4526(The)N +869(buffer)X +1090(pool)X +1256(size)X +1404(was)X +1552(varied)X +1776(from)X +1955(0)X +2018(\(the)X +2166(minimum)X +720 4614(number)N +986(of)X +1074(pages)X +1277(required)X +1565(to)X +1647(be)X +1743(buffered\))X +2063(to)X +2145(1M.)X +2316(With)X +720 4702(1M)N +854(of)X +944(buffer)X +1164(space,)X +1386(the)X +1507(package)X +1794(performed)X +2151(no)X +2253(I/O)X +2382(for)X +720 4790(this)N +871(data)X +1040(set.)X +1204(As)X +1328(Figure)X +1572(7)X +1647(illustrates,)X +2013(increasing)X +2378(the)X +720 4878(buffer)N +944(pool)X +1113(size)X +1265(can)X +1404(have)X +1583(a)X +1646(dramatic)X +1954(affect)X +2165(on)X +2271(result-)X +720 4966(ing)N +842(performance.)X +2 f +8 s +1269 4941(7)N +1 f +16 s +720 5353 MXY +864 0 Dl +2 f +8 s +760 5408(7)N +1 f +9 s +826 5433(Some)N +1024(allocators)X +1338(are)X +1460(extremely)X +1782(inef\256cient)X +2107(at)X +2192(allocating)X +720 5513(memory.)N +1029(If)X +1110(you)X +1251(\256nd)X +1396(that)X +1536(applications)X +1916(are)X +2036(running)X +2292(out)X +2416(of)X +720 5593(memory)N +1005(before)X +1234(you)X +1386(think)X +1578(they)X +1746(should,)X +2000(try)X +2124(varying)X +2388(the)X +720 5673(pagesize)N +986(to)X +1060(get)X +1166(better)X +1348(utilization)X +1658(from)X +1816(the)X +1922(memory)X +2180(allocator.)X +10 s +2830 1975 MXY +0 -28 Dl +28 0 Dl +0 28 Dl +-28 0 Dl +2853 2004 MXY +0 -27 Dl +28 0 Dl +0 27 Dl +-28 0 Dl +2876 2016 MXY +0 -27 Dl +27 0 Dl +0 27 Dl +-27 0 Dl +2922 1998 MXY +0 -27 Dl +27 0 Dl +0 27 Dl +-27 0 Dl +2967 2025 MXY +0 -28 Dl +28 0 Dl +0 28 Dl +-28 0 Dl +3013 2031 MXY +0 -28 Dl +28 0 Dl +0 28 Dl +-28 0 Dl +3059 MX +0 -28 Dl +27 0 Dl +0 28 Dl +-27 0 Dl +3196 2052 MXY +0 -28 Dl +27 0 Dl +0 28 Dl +-27 0 Dl +3561 2102 MXY +0 -28 Dl +28 0 Dl +0 28 Dl +-28 0 Dl +4292 2105 MXY +0 -28 Dl +27 0 Dl +0 28 Dl +-27 0 Dl +4 Ds +1 Dt +2844 1961 MXY +23 30 Dl +23 12 Dl +45 -18 Dl +46 26 Dl +46 6 Dl +45 0 Dl +137 21 Dl +366 50 Dl +730 3 Dl +9 s +4227 2158(User)N +-1 Ds +3 Dt +2830 1211 MXY +27 Dc +2853 1261 MXY +27 Dc +2876 1267 MXY +27 Dc +2921 1341 MXY +27 Dc +2967 1385 MXY +27 Dc +3013 1450 MXY +27 Dc +3059 1497 MXY +27 Dc +3196 1686 MXY +27 Dc +3561 2109 MXY +27 Dc +4292 2295 MXY +27 Dc +20 Ds +1 Dt +2844 1211 MXY +23 50 Dl +23 6 Dl +45 74 Dl +46 44 Dl +46 65 Dl +45 47 Dl +137 189 Dl +366 423 Dl +730 186 Dl +4181 2270(System)N +-1 Ds +3 Dt +2844 583 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2867 672 MXY +0 27 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +2890 701 MXY +0 28 Dl +0 -14 Dl +13 0 Dl +-27 0 Dl +2935 819 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-27 0 Dl +2981 849 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +3027 908 MXY +0 27 Dl +0 -13 Dl +14 0 Dl +-28 0 Dl +3072 1026 MXY +0 27 Dl +0 -13 Dl +14 0 Dl +-27 0 Dl +3209 1292 MXY +0 27 Dl +0 -14 Dl +14 0 Dl +-27 0 Dl +3575 1823 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-28 0 Dl +4305 2059 MXY +0 28 Dl +0 -14 Dl +14 0 Dl +-27 0 Dl +5 Dt +2844 597 MXY +23 88 Dl +23 30 Dl +45 118 Dl +46 30 Dl +46 59 Dl +45 118 Dl +137 265 Dl +366 532 Dl +730 236 Dl +4328 2103(Total)N +2844 2310 MXY +1461 0 Dl +2844 MX +0 -1772 Dl +2310 MY +0 18 Dl +4 Ds +1 Dt +2310 MY +0 -1772 Dl +2826 2416(0)N +-1 Ds +5 Dt +3209 2310 MXY +0 18 Dl +4 Ds +1 Dt +2310 MY +0 -1772 Dl +3155 2416(256)N +-1 Ds +5 Dt +3575 2310 MXY +0 18 Dl +4 Ds +1 Dt +2310 MY +0 -1772 Dl +3521 2416(512)N +-1 Ds +5 Dt +3940 2310 MXY +0 18 Dl +4 Ds +1 Dt +2310 MY +0 -1772 Dl +3886 2416(768)N +-1 Ds +5 Dt +4305 2310 MXY +0 18 Dl +4 Ds +1 Dt +2310 MY +0 -1772 Dl +4233 2416(1024)N +-1 Ds +5 Dt +2844 2310 MXY +-18 0 Dl +4 Ds +1 Dt +2844 MX +1461 0 Dl +2771 2340(0)N +-1 Ds +5 Dt +2844 2014 MXY +-18 0 Dl +2844 1719 MXY +-18 0 Dl +4 Ds +1 Dt +2844 MX +1461 0 Dl +2735 1749(20)N +-1 Ds +5 Dt +2844 1423 MXY +-18 0 Dl +2844 1128 MXY +-18 0 Dl +4 Ds +1 Dt +2844 MX +1461 0 Dl +2735 1158(40)N +-1 Ds +5 Dt +2844 833 MXY +-18 0 Dl +2844 538 MXY +-18 0 Dl +4 Ds +1 Dt +2844 MX +1461 0 Dl +2735 568(60)N +3239 2529(Buffer)N +3445(Pool)X +3595(Size)X +3737(\(in)X +3835(K\))X +2695 1259(S)N +2699 1324(e)N +2699 1388(c)N +2697 1452(o)N +2697 1517(n)N +2697 1581(d)N +2701 1645(s)N +3 Dt +-1 Ds +3 f +8 s +2706 2773(Figure)N +2908(7:)X +1 f +2982(User)X +3123(time)X +3258(is)X +3322(virtually)X +3560(insensitive)X +3854(to)X +3924(the)X +4022(amount)X +4234(of)X +4307(buffer)X +2706 2861(pool)N +2852(available,)X +3130(however,)X +3396(both)X +3541(system)X +3750(time)X +3895(and)X +4018(elapsed)X +4240(time)X +4385(are)X +2706 2949(inversely)N +2960(proportional)X +3296(to)X +3366(the)X +3464(size)X +3583(of)X +3656(the)X +3753(buffer)X +3927(pool.)X +4092(Even)X +4242(for)X +4335(large)X +2706 3037(data)N +2831(sets)X +2946(where)X +3120(one)X +3230(expects)X +3439(few)X +3552(collisions,)X +3832(specifying)X +4116(a)X +4162(large)X +4307(buffer)X +2706 3125(pool)N +2836(dramatically)X +3171(improves)X +3425(performance.)X +10 s +10 f +2706 3301 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +3175 3543(Enhanced)N +3536(Functionality)X +1 f +2878 3675(This)N +3046(hashing)X +3320(package)X +3609(provides)X +3910(a)X +3971(set)X +4085(of)X +4177(compati-)X +2706 3763(bility)N +2895(routines)X +3174(to)X +3257(implement)X +3620(the)X +2 f +3739(ndbm)X +1 f +3937(interface.)X +4279(How-)X +2706 3851(ever,)N +2893(when)X +3095(the)X +3220(native)X +3443(interface)X +3752(is)X +3832(used,)X +4026(the)X +4151(following)X +2706 3939(additional)N +3046(functionality)X +3475(is)X +3548(provided:)X +10 f +2798 4071(g)N +1 f +2946(Inserts)X +3197(never)X +3413(fail)X +3556(because)X +3847(too)X +3985(many)X +4199(keys)X +2946 4159(hash)N +3113(to)X +3195(the)X +3313(same)X +3498(value.)X +10 f +2798 4247(g)N +1 f +2946(Inserts)X +3187(never)X +3393(fail)X +3527(because)X +3808(key)X +3950(and/or)X +4181(asso-)X +2946 4335(ciated)N +3158(data)X +3312(is)X +3385(too)X +3507(large)X +10 f +2798 4423(g)N +1 f +2946(Hash)X +3131(functions)X +3449(may)X +3607(be)X +3703(user-speci\256ed.)X +10 f +2798 4511(g)N +1 f +2946(Multiple)X +3268(pages)X +3498(may)X +3683(be)X +3806(cached)X +4077(in)X +4186(main)X +2946 4599(memory.)N +2706 4731(It)N +2801(also)X +2976(provides)X +3298(a)X +3380(set)X +3514(of)X +3626(compatibility)X +4097(routines)X +4400(to)X +2706 4819(implement)N +3087(the)X +2 f +3224(hsearch)X +1 f +3516(interface.)X +3876(Again,)X +4130(the)X +4266(native)X +2706 4907(interface)N +3008(offers)X +3216(enhanced)X +3540(functionality:)X +10 f +2798 5039(g)N +1 f +2946(Files)X +3121(may)X +3279(grow)X +3464(beyond)X +2 f +3720(nelem)X +1 f +3932(elements.)X +10 f +2798 5127(g)N +1 f +2946(Multiple)X +3247(hash)X +3420(tables)X +3632(may)X +3795(be)X +3896(accessed)X +4203(con-)X +2946 5215(currently.)N +10 f +2798 5303(g)N +1 f +2946(Hash)X +3134(tables)X +3344(may)X +3505(be)X +3604(stored)X +3823(and)X +3962(accessed)X +4266(on)X +2946 5391(disk.)N +10 f +2798 5479(g)N +1 f +2946(Hash)X +3155(functions)X +3497(may)X +3679(be)X +3799(user-speci\256ed)X +4288(at)X +2946 5567(runtime.)N +3 f +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4424(9)X + +10 p +%%Page: 10 10 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +459 538(Relative)N +760(Performance)X +1227(of)X +1314(the)X +1441(New)X +1613(Implementation)X +1 f +604 670(The)N +761(performance)X +1200(testing)X +1445(of)X +1544(the)X +1674(new)X +1840(package)X +2135(is)X +432 758(divided)N +711(into)X +874(two)X +1033(test)X +1183(suites.)X +1424(The)X +1588(\256rst)X +1751(suite)X +1941(of)X +2046(tests)X +432 846(requires)N +727(that)X +882(the)X +1015(tables)X +1237(be)X +1348(read)X +1522(from)X +1713(and)X +1864(written)X +2126(to)X +432 934(disk.)N +640(In)X +742(these)X +942(tests,)X +1139(the)X +1272(basis)X +1467(for)X +1595(comparison)X +2003(is)X +2090(the)X +432 1022(4.3BSD-Reno)N +908(version)X +1169(of)X +2 f +1260(ndbm)X +1 f +1438(.)X +1502(Based)X +1722(on)X +1826(the)X +1948(designs)X +432 1110(of)N +2 f +521(sdbm)X +1 f +712(and)X +2 f +850(gdbm)X +1 f +1028(,)X +1070(they)X +1230(are)X +1351(expected)X +1659(to)X +1743(perform)X +2024(simi-)X +432 1198(larly)N +605(to)X +2 f +693(ndbm)X +1 f +871(,)X +917(and)X +1059(we)X +1179(do)X +1285(not)X +1413(show)X +1608(their)X +1781(performance)X +432 1286(numbers.)N +800(The)X +977(second)X +1252(suite)X +1454(contains)X +1772(the)X +1921(memory)X +432 1374(resident)N +712(test)X +849(which)X +1071(does)X +1243(not)X +1370(require)X +1623(that)X +1768(the)X +1891(\256les)X +2049(ever)X +432 1462(be)N +533(written)X +784(to)X +870(disk,)X +1047(only)X +1213(that)X +1357(hash)X +1528(tables)X +1739(may)X +1901(be)X +2001(mani-)X +432 1550(pulated)N +692(in)X +778(main)X +961(memory.)X +1291(In)X +1381(this)X +1519(test,)X +1673(we)X +1790(compare)X +2090(the)X +432 1638(performance)N +859(to)X +941(that)X +1081(of)X +1168(the)X +2 f +1286(hsearch)X +1 f +1560(routines.)X +604 1752(For)N +760(both)X +947(suites,)X +1194(two)X +1358(different)X +1679(databases)X +2031(were)X +432 1840(used.)N +656(The)X +818(\256rst)X +979(is)X +1069(the)X +1204(dictionary)X +1566(database)X +1880(described)X +432 1928(previously.)N +836(The)X +987(second)X +1236(was)X +1386(constructed)X +1781(from)X +1962(a)X +2023(pass-)X +432 2016(word)N +647(\256le)X +799(with)X +990(approximately)X +1502(300)X +1671(accounts.)X +2041(Two)X +432 2104(records)N +700(were)X +887(constructed)X +1287(for)X +1411(each)X +1589(account.)X +1909(The)X +2064(\256rst)X +432 2192(used)N +604(the)X +727(logname)X +1028(as)X +1120(the)X +1243(key)X +1384(and)X +1525(the)X +1648(remainder)X +1999(of)X +2090(the)X +432 2280(password)N +768(entry)X +965(for)X +1091(the)X +1221(data.)X +1427(The)X +1584(second)X +1839(was)X +1996(keyed)X +432 2368(by)N +541(uid)X +672(and)X +817(contained)X +1157(the)X +1283(entire)X +1494(password)X +1825(entry)X +2018(as)X +2113(its)X +432 2456(data)N +589(\256eld.)X +794(The)X +942(tests)X +1107(were)X +1287(all)X +1389(run)X +1518(on)X +1620(the)X +1740(HP)X +1864(9000)X +2046(with)X +432 2544(the)N +574(same)X +783(con\256guration)X +1254(previously)X +1636(described.)X +2027(Each)X +432 2632(test)N +576(was)X +734(run)X +874(\256ve)X +1027(times)X +1232(and)X +1380(the)X +1510(timing)X +1750(results)X +1991(of)X +2090(the)X +432 2720(runs)N +602(were)X +791(averaged.)X +1154(The)X +1311(variance)X +1616(across)X +1849(the)X +1979(5)X +2050(runs)X +432 2808(was)N +591(approximately)X +1088(1%)X +1229(of)X +1330(the)X +1462(average)X +1746(yielding)X +2041(95%)X +432 2896(con\256dence)N +800(intervals)X +1096(of)X +1183(approximately)X +1666(2%.)X +3 f +1021 3050(Disk)N +1196(Based)X +1420(Tests)X +1 f +604 3182(In)N +693(these)X +880(tests,)X +1064(we)X +1180(use)X +1308(a)X +1365(bucket)X +1600(size)X +1746(of)X +1834(1024)X +2015(and)X +2152(a)X +432 3270(\256ll)N +540(factor)X +748(of)X +835(32.)X +3 f +432 3384(create)N +663(test)X +1 f +547 3498(The)N +703(keys)X +881(are)X +1011(entered)X +1279(into)X +1433(the)X +1561(hash)X +1738(table,)X +1944(and)X +2090(the)X +547 3586(\256le)N +669(is)X +742(\257ushed)X +993(to)X +1075(disk.)X +3 f +432 3700(read)N +608(test)X +1 f +547 3814(A)N +640(lookup)X +897(is)X +984(performed)X +1353(for)X +1481(each)X +1663(key)X +1813(in)X +1909(the)X +2041(hash)X +547 3902(table.)N +3 f +432 4016(verify)N +653(test)X +1 f +547 4130(A)N +640(lookup)X +897(is)X +984(performed)X +1353(for)X +1481(each)X +1663(key)X +1813(in)X +1909(the)X +2041(hash)X +547 4218(table,)N +759(and)X +911(the)X +1045(data)X +1215(returned)X +1519(is)X +1608(compared)X +1961(against)X +547 4306(that)N +687(originally)X +1018(stored)X +1234(in)X +1316(the)X +1434(hash)X +1601(table.)X +3 f +432 4420(sequential)N +798(retrieve)X +1 f +547 4534(All)N +674(keys)X +846(are)X +970(retrieved)X +1281(in)X +1367(sequential)X +1716(order)X +1910(from)X +2090(the)X +547 4622(hash)N +724(table.)X +950(The)X +2 f +1105(ndbm)X +1 f +1313(interface)X +1625(allows)X +1863(sequential)X +547 4710(retrieval)N +848(of)X +948(the)X +1079(keys)X +1259(from)X +1448(the)X +1578(database,)X +1907(but)X +2041(does)X +547 4798(not)N +701(return)X +945(the)X +1094(data)X +1279(associated)X +1660(with)X +1853(each)X +2052(key.)X +547 4886(Therefore,)N +929(we)X +1067(compare)X +1388(the)X +1530(performance)X +1980(of)X +2090(the)X +547 4974(new)N +703(package)X +989(to)X +1073(two)X +1215(different)X +1514(runs)X +1674(of)X +2 f +1763(ndbm)X +1 f +1941(.)X +2002(In)X +2090(the)X +547 5062(\256rst)N +697(case,)X +2 f +882(ndbm)X +1 f +1086(returns)X +1335(only)X +1503(the)X +1627(keys)X +1800(while)X +2003(in)X +2090(the)X +547 5150(second,)N +2 f +823(ndbm)X +1 f +1034(returns)X +1290(both)X +1465(the)X +1596(keys)X +1776(and)X +1924(the)X +2054(data)X +547 5238(\(requiring)N +894(a)X +956(second)X +1204(call)X +1345(to)X +1432(the)X +1555(library\).)X +1861(There)X +2074(is)X +2152(a)X +547 5326(single)N +764(run)X +897(for)X +1017(the)X +1141(new)X +1300(library)X +1539(since)X +1729(it)X +1798(returns)X +2046(both)X +547 5414(the)N +665(key)X +801(and)X +937(the)X +1055(data.)X +3 f +3014 538(In-Memory)N +3431(Test)X +1 f +2590 670(This)N +2757(test)X +2892(uses)X +3054(a)X +3114(bucket)X +3352(size)X +3501(of)X +3592(256)X +3736(and)X +3876(a)X +3936(\256ll)X +4048(fac-)X +2418 758(tor)N +2527(of)X +2614(8.)X +3 f +2418 872(create/read)N +2827(test)X +1 f +2533 986(In)N +2627(this)X +2769(test,)X +2927(a)X +2989(hash)X +3162(table)X +3344(is)X +3423(created)X +3682(by)X +3788(inserting)X +4094(all)X +2533 1074(the)N +2660(key/data)X +2961(pairs.)X +3186(Then)X +3380(a)X +3445(keyed)X +3666(retrieval)X +3963(is)X +4044(per-)X +2533 1162(formed)N +2801(for)X +2931(each)X +3115(pair,)X +3295(and)X +3446(the)X +3579(hash)X +3761(table)X +3952(is)X +4040(des-)X +2533 1250(troyed.)N +3 f +2938 1404(Performance)N +3405(Results)X +1 f +2590 1536(Figures)N +2866(8a)X +2978(and)X +3130(8b)X +3246(show)X +3451(the)X +3585(user)X +3755(time,)X +3952(system)X +2418 1624(time,)N +2608(and)X +2752(elapsed)X +3021(time)X +3191(for)X +3312(each)X +3487(test)X +3625(for)X +3746(both)X +3915(the)X +4040(new)X +2418 1712(implementation)N +2951(and)X +3098(the)X +3227(old)X +3360(implementation)X +3893(\()X +2 f +3920(hsearch)X +1 f +2418 1800(or)N +2 f +2528(ndbm)X +1 f +2706(,)X +2769(whichever)X +3147(is)X +3243(appropriate\))X +3678(as)X +3787(well)X +3967(as)X +4076(the)X +2418 1888(improvement.)N +2929(The)X +3098(improvement)X +3569(is)X +3666(expressed)X +4027(as)X +4138(a)X +2418 1976(percentage)N +2787(of)X +2874(the)X +2992(old)X +3114(running)X +3383(time:)X +0 f +8 s +2418 2275(%)N +2494(=)X +2570(100)X +2722(*)X +2798 -0.4219(\(old_time)AX +3178(-)X +3254 -0.4219(new_time\))AX +3634(/)X +3710(old_time)X +1 f +10 s +2590 2600(In)N +2700(nearly)X +2944(all)X +3067(cases,)X +3299(the)X +3439(new)X +3615(routines)X +3915(perform)X +2418 2688(better)N +2628(than)X +2793(the)X +2918(old)X +3047(routines)X +3332(\(both)X +2 f +3527(hsearch)X +1 f +3807(and)X +2 f +3949(ndbm)X +1 f +4127(\).)X +2418 2776(Although)N +2755(the)X +3 f +2888(create)X +1 f +3134(tests)X +3311(exhibit)X +3567(superior)X +3864(user)X +4032(time)X +2418 2864(performance,)N +2869(the)X +2991(test)X +3126(time)X +3292(is)X +3369(dominated)X +3731(by)X +3834(the)X +3955(cost)X +4107(of)X +2418 2952(writing)N +2677(the)X +2803(actual)X +3023(\256le)X +3153(to)X +3243(disk.)X +3444(For)X +3583(the)X +3709(large)X +3897(database)X +2418 3040(\(the)N +2564(dictionary\),)X +2957(this)X +3093(completely)X +3470(overwhelmed)X +3927(the)X +4045(sys-)X +2418 3128(tem)N +2570(time.)X +2783(However,)X +3129(for)X +3254(the)X +3383(small)X +3587(data)X +3752(base,)X +3946(we)X +4071(see)X +2418 3216(that)N +2569(differences)X +2958(in)X +3051(both)X +3224(user)X +3389(and)X +3536(system)X +3788(time)X +3960(contri-)X +2418 3304(bute)N +2576(to)X +2658(the)X +2776(superior)X +3059(performance)X +3486(of)X +3573(the)X +3691(new)X +3845(package.)X +2590 3418(The)N +3 f +2764(read)X +1 f +2920(,)X +3 f +2989(verify)X +1 f +3190(,)X +3259(and)X +3 f +3424(sequential)X +1 f +3818(results)X +4075(are)X +2418 3506(deceptive)N +2758(for)X +2883(the)X +3012(small)X +3216(database)X +3524(since)X +3720(the)X +3849(entire)X +4063(test)X +2418 3594(ran)N +2551(in)X +2643(under)X +2856(a)X +2922(second.)X +3215(However,)X +3560(on)X +3669(the)X +3796(larger)X +4013(data-)X +2418 3682(base)N +2590(the)X +3 f +2716(read)X +1 f +2900(and)X +3 f +3044(verify)X +1 f +3273(tests)X +3443(bene\256t)X +3689(from)X +3873(the)X +3999(cach-)X +2418 3770(ing)N +2546(of)X +2639(buckets)X +2910(in)X +2998(the)X +3122(new)X +3282(package)X +3571(to)X +3658(improve)X +3950(perfor-)X +2418 3858(mance)N +2666(by)X +2784(over)X +2965(80%.)X +3169(Since)X +3384(the)X +3519(\256rst)X +3 f +3680(sequential)X +1 f +4063(test)X +2418 3946(does)N +2598(not)X +2733(require)X +2 f +2994(ndbm)X +1 f +3205(to)X +3299(return)X +3523(the)X +3653(data)X +3819(values,)X +4076(the)X +2418 4034(user)N +2573(time)X +2735(is)X +2808(lower)X +3011(than)X +3169(for)X +3283(the)X +3401(new)X +3555(package.)X +3879(However)X +2418 4122(when)N +2613(we)X +2728(require)X +2977(both)X +3139(packages)X +3454(to)X +3536(return)X +3748(data,)X +3922(the)X +4040(new)X +2418 4210(package)N +2702(excels)X +2923(in)X +3005(all)X +3105(three)X +3286(timings.)X +2590 4324(The)N +2773(small)X +3003(database)X +3337(runs)X +3532(so)X +3660(quickly)X +3957(in)X +4076(the)X +2418 4412(memory-resident)N +3000(case)X +3173(that)X +3326(the)X +3457(results)X +3699(are)X +3831(uninterest-)X +2418 4500(ing.)N +2589(However,)X +2933(for)X +3056(the)X +3183(larger)X +3400(database)X +3706(the)X +3833(new)X +3995(pack-)X +2418 4588(age)N +2567(pays)X +2751(a)X +2824(small)X +3033(penalty)X +3305(in)X +3403(system)X +3661(time)X +3839(because)X +4130(it)X +2418 4676(limits)N +2636(its)X +2748(main)X +2944(memory)X +3247(utilization)X +3607(and)X +3759(swaps)X +3991(pages)X +2418 4764(out)N +2550(to)X +2642(temporary)X +3002(storage)X +3264(in)X +3356(the)X +3484(\256le)X +3616(system)X +3868(while)X +4076(the)X +2 f +2418 4852(hsearch)N +1 f +2698(package)X +2988(requires)X +3273(that)X +3419(the)X +3543(application)X +3924(allocate)X +2418 4940(enough)N +2692(space)X +2909(for)X +3041(all)X +3159(key/data)X +3468(pair.)X +3670(However,)X +4022(even)X +2418 5028(with)N +2600(the)X +2738(system)X +3000(time)X +3182(penalty,)X +3477(the)X +3614(resulting)X +3933(elapsed)X +2418 5116(time)N +2580(improves)X +2898(by)X +2998(over)X +3161(50%.)X +3 f +432 5960(10)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +11 p +%%Page: 11 11 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +1 f +10 f +908 454(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2 f +1379 546(hash)N +1652(ndbm)X +1950(%change)X +1 f +10 f +908 550(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +948 642(CREATE)N +10 f +908 646(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1125 738(user)N +1424(6.4)X +1671(12.2)X +2073(48)X +1157 826(sys)N +1384(32.5)X +1671(34.7)X +2113(6)X +3 f +1006 914(elapsed)N +10 f +1310 922(c)N +890(c)Y +810(c)Y +730(c)Y +3 f +1384 914(90.4)N +10 f +1581 922(c)N +890(c)Y +810(c)Y +730(c)Y +3 f +1671 914(99.6)N +10 f +1883 922(c)N +890(c)Y +810(c)Y +730(c)Y +3 f +2113 914(9)N +1 f +10 f +908 910(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +908 926(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +948 1010(READ)N +10 f +908 1014(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1125 1106(user)N +1424(3.4)X +1711(6.1)X +2073(44)X +1157 1194(sys)N +1424(1.2)X +1671(15.3)X +2073(92)X +3 f +1006 1282(elapsed)N +10 f +1310 1290(c)N +1258(c)Y +1178(c)Y +1098(c)Y +3 f +1424 1282(4.0)N +10 f +1581 1290(c)N +1258(c)Y +1178(c)Y +1098(c)Y +3 f +1671 1282(21.2)N +10 f +1883 1290(c)N +1258(c)Y +1178(c)Y +1098(c)Y +3 f +2073 1282(81)N +1 f +10 f +908 1278(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +908 1294(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +948 1378(VERIFY)N +10 f +908 1382(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1125 1474(user)N +1424(3.5)X +1711(6.3)X +2073(44)X +1157 1562(sys)N +1424(1.2)X +1671(15.3)X +2073(92)X +3 f +1006 1650(elapsed)N +10 f +1310 1658(c)N +1626(c)Y +1546(c)Y +1466(c)Y +3 f +1424 1650(4.0)N +10 f +1581 1658(c)N +1626(c)Y +1546(c)Y +1466(c)Y +3 f +1671 1650(21.2)N +10 f +1883 1658(c)N +1626(c)Y +1546(c)Y +1466(c)Y +3 f +2073 1650(81)N +1 f +10 f +908 1646(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +908 1662(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +948 1746(SEQUENTIAL)N +10 f +908 1750(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1125 1842(user)N +1424(2.7)X +1711(1.9)X +2046(-42)X +1157 1930(sys)N +1424(0.7)X +1711(3.9)X +2073(82)X +3 f +1006 2018(elapsed)N +10 f +1310 2026(c)N +1994(c)Y +1914(c)Y +1834(c)Y +3 f +1424 2018(3.0)N +10 f +1581 2026(c)N +1994(c)Y +1914(c)Y +1834(c)Y +3 f +1711 2018(5.0)N +10 f +1883 2026(c)N +1994(c)Y +1914(c)Y +1834(c)Y +3 f +2073 2018(40)N +1 f +10 f +908 2014(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +908 2030(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +948 2114(SEQUENTIAL)N +1467(\(with)X +1656(data)X +1810(retrieval\))X +10 f +908 2118(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1125 2210(user)N +1424(2.7)X +1711(8.2)X +2073(67)X +1157 2298(sys)N +1424(0.7)X +1711(4.3)X +2073(84)X +3 f +1006 2386(elapsed)N +1424(3.0)X +1671(12.0)X +2073(75)X +1 f +10 f +908 2390(i)N +927(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +899 2394(c)N +2378(c)Y +2298(c)Y +2218(c)Y +2138(c)Y +2058(c)Y +1978(c)Y +1898(c)Y +1818(c)Y +1738(c)Y +1658(c)Y +1578(c)Y +1498(c)Y +1418(c)Y +1338(c)Y +1258(c)Y +1178(c)Y +1098(c)Y +1018(c)Y +938(c)Y +858(c)Y +778(c)Y +698(c)Y +618(c)Y +538(c)Y +1310 2394(c)N +2362(c)Y +2282(c)Y +2202(c)Y +1581 2394(c)N +2362(c)Y +2282(c)Y +2202(c)Y +1883 2394(c)N +2362(c)Y +2282(c)Y +2202(c)Y +2278 2394(c)N +2378(c)Y +2298(c)Y +2218(c)Y +2138(c)Y +2058(c)Y +1978(c)Y +1898(c)Y +1818(c)Y +1738(c)Y +1658(c)Y +1578(c)Y +1498(c)Y +1418(c)Y +1338(c)Y +1258(c)Y +1178(c)Y +1098(c)Y +1018(c)Y +938(c)Y +858(c)Y +778(c)Y +698(c)Y +618(c)Y +538(c)Y +905 2574(i)N +930(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2 f +1318 2666(hash)N +1585(hsearch)X +1953(%change)X +1 f +10 f +905 2670(i)N +930(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +945 2762(CREATE/READ)N +10 f +905 2766(i)N +930(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +1064 2858(user)N +1343(6.6)X +1642(17.2)X +2096(62)X +1096 2946(sys)N +1343(1.1)X +1682(0.3)X +2029(-266)X +3 f +945 3034(elapsed)N +1343(7.8)X +1642(17.0)X +2096(54)X +1 f +10 f +905 3038(i)N +930(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +896 3050(c)N +2978(c)Y +2898(c)Y +2818(c)Y +2738(c)Y +2658(c)Y +1249 3034(c)N +3010(c)Y +2930(c)Y +2850(c)Y +1520 3034(c)N +3010(c)Y +2930(c)Y +2850(c)Y +1886 3034(c)N +3010(c)Y +2930(c)Y +2850(c)Y +2281 3050(c)N +2978(c)Y +2898(c)Y +2818(c)Y +2738(c)Y +2658(c)Y +3 f +720 3174(Figure)N +967(8a:)X +1 f +1094(Timing)X +1349(results)X +1578(for)X +1692(the)X +1810(dictionary)X +2155(database.)X +10 f +720 3262 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +1407 3504(Conclusion)N +1 f +892 3636(This)N +1063(paper)X +1271(has)X +1407(presented)X +1744(the)X +1871(design,)X +2129(implemen-)X +720 3724(tation)N +928(and)X +1070(performance)X +1503(of)X +1596(a)X +1658(new)X +1818(hashing)X +2093(package)X +2382(for)X +720 3812(UNIX.)N +993(The)X +1150(new)X +1316(package)X +1612(provides)X +1919(a)X +1986(superset)X +2280(of)X +2378(the)X +720 3900(functionality)N +1159(of)X +1255(existing)X +1537(hashing)X +1815(packages)X +2139(and)X +2284(incor-)X +720 3988(porates)N +975(additional)X +1318(features)X +1596(such)X +1766(as)X +1855(large)X +2038(key)X +2176(handling,)X +720 4076(user)N +876(de\256ned)X +1134(hash)X +1302(functions,)X +1641(multiple)X +1928(hash)X +2096(tables,)X +2324(vari-)X +720 4164(able)N +894(sized)X +1099(pages,)X +1342(and)X +1498(linear)X +1721(hashing.)X +2050(In)X +2156(nearly)X +2396(all)X +720 4252(cases,)N +954(the)X +1096(new)X +1274(package)X +1582(provides)X +1902(improved)X +2252(perfor-)X +720 4340(mance)N +974(on)X +1098(the)X +1240(order)X +1454(of)X +1565(50-80%)X +1863(for)X +2001(the)X +2142(workloads)X +720 4428(shown.)N +990(Applications)X +1420(such)X +1588(as)X +1676(the)X +1794(loader,)X +2035(compiler,)X +2360(and)X +720 4516(mail,)N +921(which)X +1156(currently)X +1485(implement)X +1866(their)X +2051(own)X +2227(hashing)X +720 4604(routines,)N +1032(should)X +1279(be)X +1389(modi\256ed)X +1706(to)X +1801(use)X +1941(the)X +2072(generic)X +2342(rou-)X +720 4692(tines.)N +892 4806(This)N +1087(hashing)X +1389(package)X +1705(is)X +1810(one)X +1978(access)X +2236(method)X +720 4894(which)N +953(is)X +1043(part)X +1205(of)X +1309(a)X +1382(generic)X +1656(database)X +1970(access)X +2212(package)X +720 4982(being)N +955(developed)X +1342(at)X +1457(the)X +1612(University)X +2007(of)X +2131(California,)X +720 5070(Berkeley.)N +1089(It)X +1177(will)X +1340(include)X +1614(a)X +1688(btree)X +1887(access)X +2131(method)X +2409(as)X +720 5158(well)N +916(as)X +1041(\256xed)X +1259(and)X +1433(variable)X +1750(length)X +2007(record)X +2270(access)X +720 5246(methods)N +1024(in)X +1119(addition)X +1414(to)X +1509(the)X +1640(hashed)X +1896(support)X +2168(presented)X +720 5334(here.)N +948(All)X +1099(of)X +1215(the)X +1361(access)X +1615(methods)X +1934(are)X +2081(based)X +2312(on)X +2440(a)X +720 5422(key/data)N +1037(pair)X +1207(interface)X +1533(and)X +1693(appear)X +1952(identical)X +2272(to)X +2378(the)X +720 5510(application)N +1121(layer,)X +1347(allowing)X +1671(application)X +2071(implementa-)X +720 5598(tions)N +906(to)X +999(be)X +1106(largely)X +1360(independent)X +1783(of)X +1881(the)X +2010(database)X +2318(type.)X +720 5686(The)N +873(package)X +1165(is)X +1246(expected)X +1560(to)X +1650(be)X +1754(an)X +1858(integral)X +2131(part)X +2284(of)X +2378(the)X +2706 538(4.4BSD)N +3006(system,)X +3293(with)X +3479(various)X +3759(standard)X +4075(applications)X +2706 626(such)N +2879(as)X +2972(more\(1\),)X +3277(sort\(1\))X +3517(and)X +3659(vi\(1\))X +3841(based)X +4050(on)X +4156(it.)X +4266(While)X +2706 714(the)N +2833(current)X +3089(design)X +3326(does)X +3501(not)X +3631(support)X +3899(multi-user)X +4256(access)X +2706 802(or)N +2804(transactions,)X +3238(they)X +3407(could)X +3616(be)X +3723(incorporated)X +4159(relatively)X +2706 890(easily.)N +10 f +2894 938(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2 f +3365 1030(hash)N +3638(ndbm)X +3936(%change)X +1 f +10 f +2894 1034(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2934 1126(CREATE)N +10 f +2894 1130(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3111 1222(user)N +3390(0.2)X +3677(0.4)X +4079(50)X +3143 1310(sys)N +3390(0.1)X +3677(1.0)X +4079(90)X +3 f +2992 1398(elapsed)N +10 f +3296 1406(c)N +1374(c)Y +1294(c)Y +1214(c)Y +3 f +3390 1398(0)N +10 f +3567 1406(c)N +1374(c)Y +1294(c)Y +1214(c)Y +3 f +3677 1398(3.2)N +10 f +3869 1406(c)N +1374(c)Y +1294(c)Y +1214(c)Y +3 f +4039 1398(100)N +1 f +10 f +2894 1394(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2894 1410(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2934 1494(READ)N +10 f +2894 1498(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3111 1590(user)N +3390(0.1)X +3677(0.1)X +4119(0)X +3143 1678(sys)N +3390(0.1)X +3677(0.4)X +4079(75)X +3 f +2992 1766(elapsed)N +10 f +3296 1774(c)N +1742(c)Y +1662(c)Y +1582(c)Y +3 f +3390 1766(0.0)N +10 f +3567 1774(c)N +1742(c)Y +1662(c)Y +1582(c)Y +3 f +3677 1766(0.0)N +10 f +3869 1774(c)N +1742(c)Y +1662(c)Y +1582(c)Y +3 f +4119 1766(0)N +1 f +10 f +2894 1762(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2894 1778(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2934 1862(VERIFY)N +10 f +2894 1866(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3111 1958(user)N +3390(0.1)X +3677(0.2)X +4079(50)X +3143 2046(sys)N +3390(0.1)X +3677(0.3)X +4079(67)X +3 f +2992 2134(elapsed)N +10 f +3296 2142(c)N +2110(c)Y +2030(c)Y +1950(c)Y +3 f +3390 2134(0.0)N +10 f +3567 2142(c)N +2110(c)Y +2030(c)Y +1950(c)Y +3 f +3677 2134(0.0)N +10 f +3869 2142(c)N +2110(c)Y +2030(c)Y +1950(c)Y +3 f +4119 2134(0)N +1 f +10 f +2894 2130(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2894 2146(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2934 2230(SEQUENTIAL)N +10 f +2894 2234(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3111 2326(user)N +3390(0.1)X +3677(0.0)X +4012(-100)X +3143 2414(sys)N +3390(0.1)X +3677(0.1)X +4119(0)X +3 f +2992 2502(elapsed)N +10 f +3296 2510(c)N +2478(c)Y +2398(c)Y +2318(c)Y +3 f +3390 2502(0.0)N +10 f +3567 2510(c)N +2478(c)Y +2398(c)Y +2318(c)Y +3 f +3677 2502(0.0)N +10 f +3869 2510(c)N +2478(c)Y +2398(c)Y +2318(c)Y +3 f +4119 2502(0)N +1 f +10 f +2894 2498(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2894 2514(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2934 2598(SEQUENTIAL)N +3453(\(with)X +3642(data)X +3796(retrieval\))X +10 f +2894 2602(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3111 2694(user)N +3390(0.1)X +3677(0.1)X +4119(0)X +3143 2782(sys)N +3390(0.1)X +3677(0.1)X +4119(0)X +3 f +2992 2870(elapsed)N +3390(0.0)X +3677(0.0)X +4119(0)X +1 f +10 f +2894 2874(i)N +2913(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2885 2878(c)N +2862(c)Y +2782(c)Y +2702(c)Y +2622(c)Y +2542(c)Y +2462(c)Y +2382(c)Y +2302(c)Y +2222(c)Y +2142(c)Y +2062(c)Y +1982(c)Y +1902(c)Y +1822(c)Y +1742(c)Y +1662(c)Y +1582(c)Y +1502(c)Y +1422(c)Y +1342(c)Y +1262(c)Y +1182(c)Y +1102(c)Y +1022(c)Y +3296 2878(c)N +2846(c)Y +2766(c)Y +2686(c)Y +3567 2878(c)N +2846(c)Y +2766(c)Y +2686(c)Y +3869 2878(c)N +2846(c)Y +2766(c)Y +2686(c)Y +4264 2878(c)N +2862(c)Y +2782(c)Y +2702(c)Y +2622(c)Y +2542(c)Y +2462(c)Y +2382(c)Y +2302(c)Y +2222(c)Y +2142(c)Y +2062(c)Y +1982(c)Y +1902(c)Y +1822(c)Y +1742(c)Y +1662(c)Y +1582(c)Y +1502(c)Y +1422(c)Y +1342(c)Y +1262(c)Y +1182(c)Y +1102(c)Y +1022(c)Y +2891 3058(i)N +2916(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2 f +3304 3150(hash)N +3571(hsearch)X +3939(%change)X +1 f +10 f +2891 3154(i)N +2916(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2931 3246(CREATE/READ)N +10 f +2891 3250(i)N +2916(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +3050 3342(user)N +3329(0.3)X +3648(0.4)X +4048(25)X +3082 3430(sys)N +3329(0.0)X +3648(0.0)X +4088(0)X +3 f +2931 3518(elapsed)N +3329(0.0)X +3648(0.0)X +4088(0)X +1 f +10 f +2891 3522(i)N +2916(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2882 3534(c)N +3462(c)Y +3382(c)Y +3302(c)Y +3222(c)Y +3142(c)Y +3235 3518(c)N +3494(c)Y +3414(c)Y +3334(c)Y +3506 3518(c)N +3494(c)Y +3414(c)Y +3334(c)Y +3872 3518(c)N +3494(c)Y +3414(c)Y +3334(c)Y +4267 3534(c)N +3462(c)Y +3382(c)Y +3302(c)Y +3222(c)Y +3142(c)Y +3 f +2706 3658(Figure)N +2953(8b:)X +1 f +3084(Timing)X +3339(results)X +3568(for)X +3682(the)X +3800(password)X +4123(database.)X +10 f +2706 3746 -0.0930(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)AN +3 f +3396 3988(References)N +1 f +2706 4120([ATT79])N +3058(AT&T,)X +3358(DBM\(3X\),)X +2 f +3773(Unix)X +3990(Programmer's)X +2878 4208(Manual,)N +3194(Seventh)X +3491(Edition,)X +3793(Volume)X +4085(1)X +1 f +(,)S +4192(January,)X +2878 4296(1979.)N +2706 4472([ATT85])N +3027(AT&T,)X +3296(HSEARCH\(BA_LIB\),)X +2 f +4053(Unix)X +4239(System)X +2878 4560(User's)N +3112(Manual,)X +3401(System)X +3644(V.3)X +1 f +3753(,)X +3793(pp.)X +3913(506-508,)X +4220(1985.)X +2706 4736([BRE73])N +3025(Brent,)X +3253(Richard)X +3537(P.,)X +3651(``Reducing)X +4041(the)X +4168(Retrieval)X +2878 4824(Time)N +3071(of)X +3162(Scatter)X +3409(Storage)X +3678(Techniques'',)X +2 f +4146(Commun-)X +2878 4912(ications)N +3175(of)X +3281(the)X +3422(ACM)X +1 f +3591(,)X +3654(Volume)X +3955(16,)X +4098(No.)X +4259(2,)X +4362(pp.)X +2878 5000(105-109,)N +3185(February,)X +3515(1973.)X +2706 5176([BSD86])N +3055(NDBM\(3\),)X +2 f +3469(4.3BSD)X +3775(Unix)X +3990(Programmer's)X +2878 5264(Manual)N +3155(Reference)X +3505(Guide)X +1 f +3701(,)X +3749(University)X +4114(of)X +4208(Califor-)X +2878 5352(nia,)N +3016(Berkeley,)X +3346(1986.)X +2706 5528([ENB88])N +3025(Enbody,)X +3319(R.)X +3417(J.,)X +3533(Du,)X +3676(H.)X +3779(C.,)X +3897(``Dynamic)X +4270(Hash-)X +2878 5616(ing)N +3034(Schemes'',)X +2 f +3427(ACM)X +3630(Computing)X +4019(Surveys)X +1 f +4269(,)X +4322(Vol.)X +2878 5704(20,)N +2998(No.)X +3136(2,)X +3216(pp.)X +3336(85-113,)X +3603(June)X +3770(1988.)X +3 f +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4384(11)X + +12 p +%%Page: 12 12 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 258(A)N +510(New)X +682(Hashing)X +985(Package)X +1290(for)X +1413(UNIX)X +3663(Seltzer)X +3920(&)X +4007(Yigit)X +1 f +432 538([FAG79])N +776(Ronald)X +1057(Fagin,)X +1308(Jurg)X +1495(Nievergelt,)X +1903(Nicholas)X +604 626(Pippenger,)N +1003(H.)X +1135(Raymond)X +1500(Strong,)X +1787(``Extendible)X +604 714(Hashing)N +901(--)X +985(A)X +1073(Fast)X +1236(Access)X +1493(Method)X +1771(for)X +1894(Dynamic)X +604 802(Files'',)N +2 f +855(ACM)X +1046(Transactions)X +1485(on)X +1586(Database)X +1914(Systems)X +1 f +2168(,)X +604 890(Volume)N +882(4,)X +962(No.)X +1100(3.,)X +1200(September)X +1563(1979,)X +1763(pp)X +1863(315-34)X +432 1066([KNU68],)N +802(Knuth,)X +1064(D.E.,)X +2 f +1273(The)X +1434(Art)X +1577(of)X +1680(Computer)X +2041(Pro-)X +604 1154(gramming)N +971(Vol.)X +1140(3:)X +1245(Sorting)X +1518(and)X +1676(Searching)X +1 f +2001(,)X +2058(sec-)X +604 1242(tions)N +779(6.3-6.4,)X +1046(pp)X +1146(481-550.)X +432 1418([LAR78])N +747(Larson,)X +1011(Per-Ake,)X +1319(``Dynamic)X +1687(Hashing'',)X +2 f +2048(BIT)X +1 f +(,)S +604 1506(Vol.)N +764(18,)X +884(1978,)X +1084(pp.)X +1204(184-201.)X +432 1682([LAR88])N +752(Larson,)X +1021(Per-Ake,)X +1335(``Dynamic)X +1709(Hash)X +1900(Tables'',)X +2 f +604 1770(Communications)N +1183(of)X +1281(the)X +1415(ACM)X +1 f +1584(,)X +1640(Volume)X +1934(31,)X +2070(No.)X +604 1858(4.,)N +704(April)X +893(1988,)X +1093(pp)X +1193(446-457.)X +432 2034([LIT80])N +731(Witold,)X +1013(Litwin,)X +1286(``Linear)X +1590(Hashing:)X +1939(A)X +2036(New)X +604 2122(Tool)N +786(for)X +911(File)X +1065(and)X +1211(Table)X +1424(Addressing'',)X +2 f +1893(Proceed-)X +604 2210(ings)N +761(of)X +847(the)X +969(6th)X +1095(International)X +1540(Conference)X +1933(on)X +2036(Very)X +604 2298(Large)N +815(Databases)X +1 f +1153(,)X +1193(1980.)X +432 2474([NEL90])N +743(Nelson,)X +1011(Philip)X +1222(A.,)X +2 f +1341(Gdbm)X +1558(1.4)X +1679(source)X +1913(distribu-)X +604 2562(tion)N +748(and)X +888(README)X +1 f +1209(,)X +1249(August)X +1500(1990.)X +432 2738([THOM90])N +840(Ken)X +1011(Thompson,)X +1410(private)X +1670(communication,)X +604 2826(Nov.)N +782(1990.)X +432 3002([TOR87])N +790(Torek,)X +1066(C.,)X +1222(``Re:)X +1470(dbm.a)X +1751(and)X +1950(ndbm.a)X +604 3090(archives'',)N +2 f +966(USENET)X +1279(newsgroup)X +1650(comp.unix)X +1 f +2002(1987.)X +432 3266([TOR88])N +760(Torek,)X +1006(C.,)X +1133(``Re:)X +1351(questions)X +1686(regarding)X +2027(data-)X +604 3354(bases)N +826(created)X +1106(with)X +1295(dbm)X +1484(and)X +1647(ndbm)X +1876(routines'')X +2 f +604 3442(USENET)N +937(newsgroup)X +1328(comp.unix.questions)X +1 f +1982(,)X +2041(June)X +604 3530(1988.)N +432 3706([WAL84])N +773(Wales,)X +1018(R.,)X +1135(``Discussion)X +1564(of)X +1655("dbm")X +1887(data)X +2045(base)X +604 3794(system'',)N +2 f +973(USENET)X +1339(newsgroup)X +1762(unix.wizards)X +1 f +2168(,)X +604 3882(January,)N +894(1984.)X +432 4058([YIG89])N +751(Ozan)X +963(S.)X +1069(Yigit,)X +1294(``How)X +1545(to)X +1648(Roll)X +1826(Your)X +2032(Own)X +604 4146(Dbm/Ndbm'',)N +2 f +1087(unpublished)X +1504(manuscript)X +1 f +(,)S +1910(Toronto,)X +604 4234(July,)N +777(1989)X +3 f +432 5960(12)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +13 p +%%Page: 13 13 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +720 258(Seltzer)N +977(&)X +1064(Yigit)X +3278(A)X +3356(New)X +3528(Hashing)X +3831(Package)X +4136(for)X +4259(UNIX)X +1 f +720 538(Margo)N +960(I.)X +1033(Seltzer)X +1282(is)X +1361(a)X +1423(Ph.D.)X +1631(student)X +1887(in)X +1974(the)X +2097(Department)X +720 626(of)N +823(Electrical)X +1167(Engineering)X +1595(and)X +1747(Computer)X +2102(Sciences)X +2418(at)X +720 714(the)N +850(University)X +1220(of)X +1318(California,)X +1694(Berkeley.)X +2055(Her)X +2207(research)X +720 802(interests)N +1017(include)X +1283(\256le)X +1415(systems,)X +1718(databases,)X +2076(and)X +2221(transac-)X +720 890(tion)N +896(processing)X +1291(systems.)X +1636(She)X +1807(spent)X +2027(several)X +2306(years)X +720 978(working)N +1026(at)X +1123(startup)X +1380(companies)X +1762(designing)X +2112(and)X +2267(imple-)X +720 1066(menting)N +1048(\256le)X +1216(systems)X +1535(and)X +1716(transaction)X +2133(processing)X +720 1154(software)N +1026(and)X +1170(designing)X +1509(microprocessors.)X +2103(Ms.)X +2253(Seltzer)X +720 1242(received)N +1057(her)X +1223(AB)X +1397(in)X +1522(Applied)X +1843(Mathematics)X +2320(from)X +720 1330 0.1953(Harvard/Radcliffe)AN +1325(College)X +1594(in)X +1676(1983.)X +720 1444(In)N +810(her)X +936(spare)X +1129(time,)X +1313(Margo)X +1549(can)X +1683(usually)X +1936(be)X +2034(found)X +2243(prepar-)X +720 1532(ing)N +868(massive)X +1171(quantities)X +1527(of)X +1639(food)X +1831(for)X +1970(hungry)X +2242(hoards,)X +720 1620(studying)N +1022(Japanese,)X +1355(or)X +1449(playing)X +1716(soccer)X +1948(with)X +2116(an)X +2218(exciting)X +720 1708(Bay)N +912(Area)X +1132(Women's)X +1507(Soccer)X +1788(team,)X +2026(the)X +2186(Berkeley)X +720 1796(Bruisers.)N +720 1910(Ozan)N +915(\()X +3 f +942(Oz)X +1 f +1040(\))X +1092(Yigit)X +1281(is)X +1358(currently)X +1672(a)X +1732(software)X +2033(engineer)X +2334(with)X +720 1998(the)N +886(Communications)X +1499(Research)X +1861(and)X +2044(Development)X +720 2086(group,)N +948(Computing)X +1328(Services,)X +1641(York)X +1826(University.)X +2224(His)X +2355(for-)X +720 2174(mative)N +967(years)X +1166(were)X +1352(also)X +1510(spent)X +1708(at)X +1795(York,)X +2009(where)X +2234(he)X +2338(held)X +720 2262(system)N +985(programmer)X +1425(and)X +1583(administrator)X +2052(positions)X +2382(for)X +720 2350(various)N +995(mixtures)X +1314(of)X +1420(of)X +1526(UNIX)X +1765(systems)X +2056(starting)X +2334(with)X +720 2438(Berkeley)N +1031(4.1)X +1151(in)X +1233(1982,)X +1433(while)X +1631(at)X +1709(the)X +1827(same)X +2012(time)X +2174(obtaining)X +720 2526(a)N +776(degree)X +1011(in)X +1093(Computer)X +1433(Science.)X +720 2640(In)N +813(his)X +931(copious)X +1205(free)X +1356(time,)X +1543(Oz)X +1662(enjoys)X +1896(working)X +2188(on)X +2293(what-)X +720 2728(ever)N +890(software)X +1197(looks)X +1400(interesting,)X +1788(which)X +2014(often)X +2209(includes)X +720 2816(language)N +1044(interpreters,)X +1464(preprocessors,)X +1960(and)X +2110(lately,)X +2342(pro-)X +720 2904(gram)N +905(generators)X +1260(and)X +1396(expert)X +1617(systems.)X +720 3018(Oz)N +836(has)X +964(authored)X +1266(several)X +1515(public-domain)X +2003(software)X +2301(tools,)X +720 3106(including)N +1069(an)X +1191(nroff-like)X +1545(text)X +1711(formatter)X +2 f +2056(proff)X +1 f +2257(that)X +2423(is)X +720 3194(apparently)N +1083(still)X +1226(used)X +1397(in)X +1483(some)X +1676(basement)X +2002(PCs.)X +2173(His)X +2307(latest)X +720 3282(obsessions)N +1143(include)X +1460(the)X +1639(incredible)X +2040(programming)X +720 3370(language)N +1030(Scheme,)X +1324(and)X +1460(Chinese)X +1738(Brush)X +1949(painting.)X +3 f +720 5960(USENIX)N +9 f +1042(-)X +3 f +1106(Winter)X +1371('91)X +9 f +1498(-)X +3 f +1562(Dallas,)X +1815(TX)X +4384(13)X + +14 p +%%Page: 14 14 +0(Courier)xf 0 f +10 s 10 xH 0 xS 0 f +3 f +432 5960(14)N +2970(USENIX)X +9 f +3292(-)X +3 f +3356(Winter)X +3621('91)X +9 f +3748(-)X +3 f +3812(Dallas,)X +4065(TX)X + +14 p +%%Trailer +xt + +xs diff --git a/src/util/db2/docs/libtp.usenix.ps b/src/util/db2/docs/libtp.usenix.ps new file mode 100644 index 000000000..5b5ba6e1b --- /dev/null +++ b/src/util/db2/docs/libtp.usenix.ps @@ -0,0 +1,12340 @@ +%!PS-Adobe-1.0 +%%Creator: utopia:margo (& Seltzer,608-13E,8072,) +%%Title: stdin (ditroff) +%%CreationDate: Thu Dec 12 15:32:11 1991 +%%EndComments +% @(#)psdit.pro 1.3 4/15/88 +% lib/psdit.pro -- prolog for psdit (ditroff) files +% Copyright (c) 1984, 1985 Adobe Systems Incorporated. All Rights Reserved. +% last edit: shore Sat Nov 23 20:28:03 1985 +% RCSID: $Header$ + +% Changed by Edward Wang (edward@ucbarpa.berkeley.edu) to handle graphics, +% 17 Feb, 87. + +/$DITroff 140 dict def $DITroff begin +/fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def +/xi{0 72 11 mul translate 72 resolution div dup neg scale 0 0 moveto + /fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def F + /pagesave save def}def +/PB{save /psv exch def currentpoint translate + resolution 72 div dup neg scale 0 0 moveto}def +/PE{psv restore}def +/arctoobig 90 def /arctoosmall .05 def +/m1 matrix def /m2 matrix def /m3 matrix def /oldmat matrix def +/tan{dup sin exch cos div}def +/point{resolution 72 div mul}def +/dround {transform round exch round exch itransform}def +/xT{/devname exch def}def +/xr{/mh exch def /my exch def /resolution exch def}def +/xp{}def +/xs{docsave restore end}def +/xt{}def +/xf{/fontname exch def /slotno exch def fontnames slotno get fontname eq not + {fonts slotno fontname findfont put fontnames slotno fontname put}if}def +/xH{/fontheight exch def F}def +/xS{/fontslant exch def F}def +/s{/fontsize exch def /fontheight fontsize def F}def +/f{/fontnum exch def F}def +/F{fontheight 0 le{/fontheight fontsize def}if + fonts fontnum get fontsize point 0 0 fontheight point neg 0 0 m1 astore + fontslant 0 ne{1 0 fontslant tan 1 0 0 m2 astore m3 concatmatrix}if + makefont setfont .04 fontsize point mul 0 dround pop setlinewidth}def +/X{exch currentpoint exch pop moveto show}def +/N{3 1 roll moveto show}def +/Y{exch currentpoint pop exch moveto show}def +/S{show}def +/ditpush{}def/ditpop{}def +/AX{3 -1 roll currentpoint exch pop moveto 0 exch ashow}def +/AN{4 2 roll moveto 0 exch ashow}def +/AY{3 -1 roll currentpoint pop exch moveto 0 exch ashow}def +/AS{0 exch ashow}def +/MX{currentpoint exch pop moveto}def +/MY{currentpoint pop exch moveto}def +/MXY{moveto}def +/cb{pop}def % action on unknown char -- nothing for now +/n{}def/w{}def +/p{pop showpage pagesave restore /pagesave save def}def +/Dt{/Dlinewidth exch def}def 1 Dt +/Ds{/Ddash exch def}def -1 Ds +/Di{/Dstipple exch def}def 1 Di +/Dsetlinewidth{2 Dlinewidth mul setlinewidth}def +/Dsetdash{Ddash 4 eq{[8 12]}{Ddash 16 eq{[32 36]} + {Ddash 20 eq{[32 12 8 12]}{[]}ifelse}ifelse}ifelse 0 setdash}def +/Dstroke{gsave Dsetlinewidth Dsetdash 1 setlinecap stroke grestore + currentpoint newpath moveto}def +/Dl{rlineto Dstroke}def +/arcellipse{/diamv exch def /diamh exch def oldmat currentmatrix pop + currentpoint translate 1 diamv diamh div scale /rad diamh 2 div def + currentpoint exch rad add exch rad -180 180 arc oldmat setmatrix}def +/Dc{dup arcellipse Dstroke}def +/De{arcellipse Dstroke}def +/Da{/endv exch def /endh exch def /centerv exch def /centerh exch def + /cradius centerv centerv mul centerh centerh mul add sqrt def + /eradius endv endv mul endh endh mul add sqrt def + /endang endv endh atan def + /startang centerv neg centerh neg atan def + /sweep startang endang sub dup 0 lt{360 add}if def + sweep arctoobig gt + {/midang startang sweep 2 div sub def /midrad cradius eradius add 2 div def + /midh midang cos midrad mul def /midv midang sin midrad mul def + midh neg midv neg endh endv centerh centerv midh midv Da + Da} + {sweep arctoosmall ge + {/controldelt 1 sweep 2 div cos sub 3 sweep 2 div sin mul div 4 mul def + centerv neg controldelt mul centerh controldelt mul + endv neg controldelt mul centerh add endh add + endh controldelt mul centerv add endv add + centerh endh add centerv endv add rcurveto Dstroke} + {centerh endh add centerv endv add rlineto Dstroke} + ifelse} + ifelse}def +/Dpatterns[ +[%cf[widthbits] +[8<0000000000000010>] +[8<0411040040114000>] +[8<0204081020408001>] +[8<0000103810000000>] +[8<6699996666999966>] +[8<0000800100001008>] +[8<81c36666c3810000>] +[8<0f0e0c0800000000>] +[8<0000000000000010>] +[8<0411040040114000>] +[8<0204081020408001>] +[8<0000001038100000>] +[8<6699996666999966>] +[8<0000800100001008>] +[8<81c36666c3810000>] +[8<0f0e0c0800000000>] +[8<0042660000246600>] +[8<0000990000990000>] +[8<0804020180402010>] +[8<2418814242811824>] +[8<6699996666999966>] +[8<8000000008000000>] +[8<00001c3e363e1c00>] +[8<0000000000000000>] +[32<00000040000000c00000004000000040000000e0000000000000000000000000>] +[32<00000000000060000000900000002000000040000000f0000000000000000000>] +[32<000000000000000000e0000000100000006000000010000000e0000000000000>] +[32<00000000000000002000000060000000a0000000f00000002000000000000000>] +[32<0000000e0000000000000000000000000000000f000000080000000e00000001>] +[32<0000090000000600000000000000000000000000000007000000080000000e00>] +[32<00010000000200000004000000040000000000000000000000000000000f0000>] +[32<0900000006000000090000000600000000000000000000000000000006000000>]] +[%ug +[8<0000020000000000>] +[8<0000020000002000>] +[8<0004020000002000>] +[8<0004020000402000>] +[8<0004060000402000>] +[8<0004060000406000>] +[8<0006060000406000>] +[8<0006060000606000>] +[8<00060e0000606000>] +[8<00060e000060e000>] +[8<00070e000060e000>] +[8<00070e000070e000>] +[8<00070e020070e000>] +[8<00070e020070e020>] +[8<04070e020070e020>] +[8<04070e024070e020>] +[8<04070e064070e020>] +[8<04070e064070e060>] +[8<06070e064070e060>] +[8<06070e066070e060>] +[8<06070f066070e060>] +[8<06070f066070f060>] +[8<060f0f066070f060>] +[8<060f0f0660f0f060>] +[8<060f0f0760f0f060>] +[8<060f0f0760f0f070>] +[8<0e0f0f0760f0f070>] +[8<0e0f0f07e0f0f070>] +[8<0e0f0f0fe0f0f070>] +[8<0e0f0f0fe0f0f0f0>] +[8<0f0f0f0fe0f0f0f0>] +[8<0f0f0f0ff0f0f0f0>] +[8<1f0f0f0ff0f0f0f0>] +[8<1f0f0f0ff1f0f0f0>] +[8<1f0f0f8ff1f0f0f0>] +[8<1f0f0f8ff1f0f0f8>] +[8<9f0f0f8ff1f0f0f8>] +[8<9f0f0f8ff9f0f0f8>] +[8<9f0f0f9ff9f0f0f8>] +[8<9f0f0f9ff9f0f0f9>] +[8<9f8f0f9ff9f0f0f9>] +[8<9f8f0f9ff9f8f0f9>] +[8<9f8f1f9ff9f8f0f9>] +[8<9f8f1f9ff9f8f1f9>] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8] +[8]] +[%mg +[8<8000000000000000>] +[8<0822080080228000>] +[8<0204081020408001>] +[8<40e0400000000000>] +[8<66999966>] +[8<8001000010080000>] +[8<81c36666c3810000>] +[8] +[16<07c00f801f003e007c00f800f001e003c007800f001f003e007c00f801f003e0>] +[16<1f000f8007c003e001f000f8007c003e001f800fc007e003f001f8007c003e00>] +[8] +[16<0040008001000200040008001000200040008000000100020004000800100020>] +[16<0040002000100008000400020001800040002000100008000400020001000080>] +[16<1fc03fe07df0f8f8f07de03fc01f800fc01fe03ff07df8f87df03fe01fc00f80>] +[8<80>] +[8<8040201000000000>] +[8<84cc000048cc0000>] +[8<9900009900000000>] +[8<08040201804020100800020180002010>] +[8<2418814242811824>] +[8<66999966>] +[8<8000000008000000>] +[8<70f8d8f870000000>] +[8<0814224180402010>] +[8] +[8<018245aa45820100>] +[8<221c224180808041>] +[8<88000000>] +[8<0855800080550800>] +[8<2844004482440044>] +[8<0810204080412214>] +[8<00>]]]def +/Dfill{ + transform /maxy exch def /maxx exch def + transform /miny exch def /minx exch def + minx maxx gt{/minx maxx /maxx minx def def}if + miny maxy gt{/miny maxy /maxy miny def def}if + Dpatterns Dstipple 1 sub get exch 1 sub get + aload pop /stip exch def /stipw exch def /stiph 128 def + /imatrix[stipw 0 0 stiph 0 0]def + /tmatrix[stipw 0 0 stiph 0 0]def + /minx minx cvi stiph idiv stiph mul def + /miny miny cvi stipw idiv stipw mul def + gsave eoclip 0 setgray + miny stiph maxy{ + tmatrix exch 5 exch put + minx stipw maxx{ + tmatrix exch 4 exch put tmatrix setmatrix + stipw stiph true imatrix {stip} imagemask + }for + }for + grestore +}def +/Dp{Dfill Dstroke}def +/DP{Dfill currentpoint newpath moveto}def +end + +/ditstart{$DITroff begin + /nfonts 60 def % NFONTS makedev/ditroff dependent! + /fonts[nfonts{0}repeat]def + /fontnames[nfonts{()}repeat]def +/docsave save def +}def + +% character outcalls +/oc{ + /pswid exch def /cc exch def /name exch def + /ditwid pswid fontsize mul resolution mul 72000 div def + /ditsiz fontsize resolution mul 72 div def + ocprocs name known{ocprocs name get exec}{name cb}ifelse +}def +/fractm [.65 0 0 .6 0 0] def +/fraction{ + /fden exch def /fnum exch def gsave /cf currentfont def + cf fractm makefont setfont 0 .3 dm 2 copy neg rmoveto + fnum show rmoveto currentfont cf setfont(\244)show setfont fden show + grestore ditwid 0 rmoveto +}def +/oce{grestore ditwid 0 rmoveto}def +/dm{ditsiz mul}def +/ocprocs 50 dict def ocprocs begin +(14){(1)(4)fraction}def +(12){(1)(2)fraction}def +(34){(3)(4)fraction}def +(13){(1)(3)fraction}def +(23){(2)(3)fraction}def +(18){(1)(8)fraction}def +(38){(3)(8)fraction}def +(58){(5)(8)fraction}def +(78){(7)(8)fraction}def +(sr){gsave 0 .06 dm rmoveto(\326)show oce}def +(is){gsave 0 .15 dm rmoveto(\362)show oce}def +(->){gsave 0 .02 dm rmoveto(\256)show oce}def +(<-){gsave 0 .02 dm rmoveto(\254)show oce}def +(==){gsave 0 .05 dm rmoveto(\272)show oce}def +(uc){gsave currentpoint 400 .009 dm mul add translate + 8 -8 scale ucseal oce}def +end + +% an attempt at a PostScript FONT to implement ditroff special chars +% this will enable us to +% cache the little buggers +% generate faster, more compact PS out of psdit +% confuse everyone (including myself)! +50 dict dup begin +/FontType 3 def +/FontName /DIThacks def +/FontMatrix [.001 0 0 .001 0 0] def +/FontBBox [-260 -260 900 900] def% a lie but ... +/Encoding 256 array def +0 1 255{Encoding exch /.notdef put}for +Encoding + dup 8#040/space put %space + dup 8#110/rc put %right ceil + dup 8#111/lt put %left top curl + dup 8#112/bv put %bold vert + dup 8#113/lk put %left mid curl + dup 8#114/lb put %left bot curl + dup 8#115/rt put %right top curl + dup 8#116/rk put %right mid curl + dup 8#117/rb put %right bot curl + dup 8#120/rf put %right floor + dup 8#121/lf put %left floor + dup 8#122/lc put %left ceil + dup 8#140/sq put %square + dup 8#141/bx put %box + dup 8#142/ci put %circle + dup 8#143/br put %box rule + dup 8#144/rn put %root extender + dup 8#145/vr put %vertical rule + dup 8#146/ob put %outline bullet + dup 8#147/bu put %bullet + dup 8#150/ru put %rule + dup 8#151/ul put %underline + pop +/DITfd 100 dict def +/BuildChar{0 begin + /cc exch def /fd exch def + /charname fd /Encoding get cc get def + /charwid fd /Metrics get charname get def + /charproc fd /CharProcs get charname get def + charwid 0 fd /FontBBox get aload pop setcachedevice + 2 setlinejoin 40 setlinewidth + newpath 0 0 moveto gsave charproc grestore + end}def +/BuildChar load 0 DITfd put +/CharProcs 50 dict def +CharProcs begin +/space{}def +/.notdef{}def +/ru{500 0 rls}def +/rn{0 840 moveto 500 0 rls}def +/vr{0 800 moveto 0 -770 rls}def +/bv{0 800 moveto 0 -1000 rls}def +/br{0 840 moveto 0 -1000 rls}def +/ul{0 -140 moveto 500 0 rls}def +/ob{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath stroke}def +/bu{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath fill}def +/sq{80 0 rmoveto currentpoint dround newpath moveto + 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath stroke}def +/bx{80 0 rmoveto currentpoint dround newpath moveto + 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath fill}def +/ci{500 360 rmoveto currentpoint newpath 333 0 360 arc + 50 setlinewidth stroke}def + +/lt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 add exch s4 a4p stroke}def +/lb{0 800 moveto 0 -550 rlineto currx -200 2cx s4 add exch s4 a4p stroke}def +/rt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 sub exch s4 a4p stroke}def +/rb{0 800 moveto 0 -500 rlineto currx -200 2cx s4 sub exch s4 a4p stroke}def +/lk{0 800 moveto 0 300 -300 300 s4 arcto pop pop 1000 sub + 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def +/rk{0 800 moveto 0 300 s2 300 s4 arcto pop pop 1000 sub + 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def +/lf{0 800 moveto 0 -1000 rlineto s4 0 rls}def +/rf{0 800 moveto 0 -1000 rlineto s4 neg 0 rls}def +/lc{0 -200 moveto 0 1000 rlineto s4 0 rls}def +/rc{0 -200 moveto 0 1000 rlineto s4 neg 0 rls}def +end + +/Metrics 50 dict def Metrics begin +/.notdef 0 def +/space 500 def +/ru 500 def +/br 0 def +/lt 416 def +/lb 416 def +/rt 416 def +/rb 416 def +/lk 416 def +/rk 416 def +/rc 416 def +/lc 416 def +/rf 416 def +/lf 416 def +/bv 416 def +/ob 350 def +/bu 350 def +/ci 750 def +/bx 750 def +/sq 750 def +/rn 500 def +/ul 500 def +/vr 0 def +end + +DITfd begin +/s2 500 def /s4 250 def /s3 333 def +/a4p{arcto pop pop pop pop}def +/2cx{2 copy exch}def +/rls{rlineto stroke}def +/currx{currentpoint pop}def +/dround{transform round exch round exch itransform} def +end +end +/DIThacks exch definefont pop +ditstart +(psc)xT +576 1 1 xr +1(Times-Roman)xf 1 f +2(Times-Italic)xf 2 f +3(Times-Bold)xf 3 f +4(Times-BoldItalic)xf 4 f +5(Helvetica)xf 5 f +6(Helvetica-Bold)xf 6 f +7(Courier)xf 7 f +8(Courier-Bold)xf 8 f +9(Symbol)xf 9 f +10(DIThacks)xf 10 f +10 s +1 f +xi +%%EndProlog + +%%Page: 1 1 +10 s 10 xH 0 xS 1 f +3 f +14 s +1205 1206(LIBTP:)N +1633(Portable,)X +2100(M)X +2206(odular)X +2551(Transactions)X +3202(for)X +3374(UNIX)X +1 f +11 s +3661 1162(1)N +2 f +12 s +2182 1398(Margo)N +2467(Seltzer)X +2171 1494(Michael)N +2511(Olson)X +1800 1590(University)N +2225(of)X +2324(California,)X +2773(Berkeley)X +3 f +2277 1878(Abstract)N +1 f +10 s +755 2001(Transactions)N +1198(provide)X +1475(a)X +1543(useful)X +1771(programming)X +2239(paradigm)X +2574(for)X +2700(maintaining)X +3114(logical)X +3364(consistency,)X +3790(arbitrating)X +4156(con-)X +555 2091(current)N +808(access,)X +1059(and)X +1200(managing)X +1540(recovery.)X +1886(In)X +1977(traditional)X +2330(UNIX)X +2555(systems,)X +2852(the)X +2974(only)X +3140(easy)X +3307(way)X +3465(of)X +3556(using)X +3753(transactions)X +4160(is)X +4237(to)X +555 2181(purchase)N +876(a)X +947(database)X +1258(system.)X +1554(Such)X +1748(systems)X +2035(are)X +2168(often)X +2367(slow,)X +2572(costly,)X +2817(and)X +2967(may)X +3139(not)X +3275(provide)X +3554(the)X +3686(exact)X +3890(functionality)X +555 2271(desired.)N +848(This)X +1011(paper)X +1210(presents)X +1493(the)X +1611(design,)X +1860(implementation,)X +2402(and)X +2538(performance)X +2965(of)X +3052(LIBTP,)X +3314(a)X +3370(simple,)X +3623(non-proprietary)X +4147(tran-)X +555 2361(saction)N +809(library)X +1050(using)X +1249(the)X +1373(4.4BSD)X +1654(database)X +1957(access)X +2189(routines)X +2473(\()X +3 f +2500(db)X +1 f +2588(\(3\)\).)X +2775(On)X +2899(a)X +2961(conventional)X +3401(transaction)X +3779(processing)X +4148(style)X +555 2451(benchmark,)N +959(its)X +1061(performance)X +1495(is)X +1575(approximately)X +2065(85%)X +2239(that)X +2386(of)X +2480(the)X +2604(database)X +2907(access)X +3139(routines)X +3423(without)X +3693(transaction)X +4071(protec-)X +555 2541(tion,)N +725(200%)X +938(that)X +1084(of)X +1177(using)X +3 f +1376(fsync)X +1 f +1554(\(2\))X +1674(to)X +1761(commit)X +2030(modi\256cations)X +2490(to)X +2577(disk,)X +2755(and)X +2896(125%)X +3108(that)X +3253(of)X +3345(a)X +3406(commercial)X +3810(relational)X +4138(data-)X +555 2631(base)N +718(system.)X +3 f +555 2817(1.)N +655(Introduction)X +1 f +755 2940(Transactions)N +1186(are)X +1306(used)X +1474(in)X +1557(database)X +1855(systems)X +2129(to)X +2212(enable)X +2443(concurrent)X +2807(users)X +2992(to)X +3074(apply)X +3272(multi-operation)X +3790(updates)X +4055(without)X +555 3030(violating)N +863(the)X +985(integrity)X +1280(of)X +1371(the)X +1493(database.)X +1814(They)X +2003(provide)X +2271(the)X +2392(properties)X +2736(of)X +2826(atomicity,)X +3171(consistency,)X +3588(isolation,)X +3906(and)X +4045(durabil-)X +555 3120(ity.)N +701(By)X +816(atomicity,)X +1160(we)X +1276(mean)X +1472(that)X +1614(the)X +1734(set)X +1845(of)X +1934(updates)X +2200(comprising)X +2581(a)X +2638(transaction)X +3011(must)X +3187(be)X +3284(applied)X +3541(as)X +3629(a)X +3686(single)X +3898(unit;)X +4085(that)X +4226(is,)X +555 3210(they)N +714(must)X +890(either)X +1094(all)X +1195(be)X +1292(applied)X +1549(to)X +1632(the)X +1751(database)X +2049(or)X +2137(all)X +2238(be)X +2335(absent.)X +2601(Consistency)X +3013(requires)X +3293(that)X +3434(a)X +3491(transaction)X +3864(take)X +4019(the)X +4138(data-)X +555 3300(base)N +725(from)X +908(one)X +1051(logically)X +1358(consistent)X +1704(state)X +1877(to)X +1965(another.)X +2272(The)X +2423(property)X +2721(of)X +2814(isolation)X +3115(requires)X +3400(that)X +3546(concurrent)X +3916(transactions)X +555 3390(yield)N +750(results)X +994(which)X +1225(are)X +1358(indistinguishable)X +1938(from)X +2128(the)X +2260(results)X +2503(which)X +2733(would)X +2967(be)X +3077(obtained)X +3387(by)X +3501(running)X +3784(the)X +3916(transactions)X +555 3480(sequentially.)N +1002(Finally,)X +1268(durability)X +1599(requires)X +1878(that)X +2018(once)X +2190(transactions)X +2593(have)X +2765(been)X +2937(committed,)X +3319(their)X +3486(results)X +3715(must)X +3890(be)X +3986(preserved)X +555 3570(across)N +776(system)X +1018(failures)X +1279([TPCB90].)X +755 3693(Although)N +1080(these)X +1268(properties)X +1612(are)X +1734(most)X +1912(frequently)X +2265(discussed)X +2595(in)X +2680(the)X +2801(context)X +3060(of)X +3150(databases,)X +3501(they)X +3661(are)X +3782(useful)X +4000(program-)X +555 3783(ming)N +750(paradigms)X +1114(for)X +1238(more)X +1433(general)X +1700(purpose)X +1984(applications.)X +2441(There)X +2659(are)X +2788(several)X +3046(different)X +3353(situations)X +3689(where)X +3916(transactions)X +555 3873(can)N +687(be)X +783(used)X +950(to)X +1032(replace)X +1285(current)X +1533(ad-hoc)X +1772(mechanisms.)X +755 3996(One)N +910(situation)X +1206(is)X +1280(when)X +1475(multiple)X +1762(\256les)X +1916(or)X +2004(parts)X +2181(of)X +2269(\256les)X +2422(need)X +2594(to)X +2676(be)X +2772(updated)X +3046(in)X +3128(an)X +3224(atomic)X +3462(fashion.)X +3758(For)X +3889(example,)X +4201(the)X +555 4086(traditional)N +907(UNIX)X +1131(\256le)X +1256(system)X +1501(uses)X +1661(ordering)X +1955(constraints)X +2324(to)X +2408(achieve)X +2676(recoverability)X +3144(in)X +3228(the)X +3348(face)X +3505(of)X +3594(crashes.)X +3893(When)X +4107(a)X +4165(new)X +555 4176(\256le)N +678(is)X +752(created,)X +1026(its)X +1122(inode)X +1321(is)X +1395(written)X +1642(to)X +1724(disk)X +1877(before)X +2103(the)X +2221(new)X +2375(\256le)X +2497(is)X +2570(added)X +2782(to)X +2864(the)X +2982(directory)X +3292(structure.)X +3633(This)X +3795(guarantees)X +4159(that,)X +555 4266(if)N +627(the)X +748(system)X +993(crashes)X +1253(between)X +1544(the)X +1665(two)X +1808(I/O's,)X +2016(the)X +2137(directory)X +2450(does)X +2620(not)X +2744(contain)X +3002(a)X +3060 0.4531(reference)AX +3383(to)X +3467(an)X +3565(invalid)X +3809(inode.)X +4049(In)X +4138(actu-)X +555 4356(ality,)N +741(the)X +863(desired)X +1119(effect)X +1326(is)X +1402(that)X +1545(these)X +1733(two)X +1876(updates)X +2144(have)X +2319(the)X +2440(transactional)X +2873(property)X +3168(of)X +3258(atomicity)X +3583(\(either)X +3816(both)X +3981(writes)X +4200(are)X +555 4446(visible)N +790(or)X +879(neither)X +1124(is\).)X +1266(Rather)X +1501(than)X +1660(building)X +1947(special)X +2191(purpose)X +2466(recovery)X +2769(mechanisms)X +3186(into)X +3331(the)X +3450(\256le)X +3573(system)X +3816(or)X +3904(related)X +4144(tools)X +555 4536(\()N +2 f +582(e.g.)X +3 f +726(fsck)X +1 f +864(\(8\)\),)X +1033(one)X +1177(could)X +1383(use)X +1518(general)X +1783(purpose)X +2064(transaction)X +2443(recovery)X +2752(protocols)X +3077(after)X +3252(system)X +3501(failure.)X +3778(Any)X +3943(application)X +555 4626(that)N +705(needs)X +918(to)X +1010(keep)X +1192(multiple,)X +1508(related)X +1757(\256les)X +1920(\(or)X +2044(directories\))X +2440(consistent)X +2790(should)X +3032(do)X +3141(so)X +3241(using)X +3443(transactions.)X +3895(Source)X +4147(code)X +555 4716(control)N +805(systems,)X +1101(such)X +1271(as)X +1361(RCS)X +1534(and)X +1673(SCCS,)X +1910(should)X +2146(use)X +2276(transaction)X +2651(semantics)X +2990(to)X +3075(allow)X +3276(the)X +3397(``checking)X +3764(in'')X +3903(of)X +3992(groups)X +4232(of)X +555 4806(related)N +801(\256les.)X +1001(In)X +1095(this)X +1237(way,)X +1418(if)X +1493(the)X +1617 0.2841(``check-in'')AX +2028(fails,)X +2212(the)X +2336(transaction)X +2714(may)X +2878(be)X +2980(aborted,)X +3267(backing)X +3547(out)X +3675(the)X +3799(partial)X +4030(``check-)X +555 4896(in'')N +691(leaving)X +947(the)X +1065(source)X +1295(repository)X +1640(in)X +1722(a)X +1778(consistent)X +2118(state.)X +755 5019(A)N +842(second)X +1094(situation)X +1398(where)X +1624(transactions)X +2036(can)X +2177(be)X +2282(used)X +2458(to)X +2549(replace)X +2811(current)X +3068(ad-hoc)X +3316(mechanisms)X +3741(is)X +3822(in)X +3912(applications)X +555 5109(where)N +776(concurrent)X +1144(updates)X +1413(to)X +1499(a)X +1559(shared)X +1793(\256le)X +1919(are)X +2042(desired,)X +2318(but)X +2444(there)X +2629(is)X +2706(logical)X +2948(consistency)X +3345(of)X +3435(the)X +3556(data)X +3713(which)X +3932(needs)X +4138(to)X +4223(be)X +555 5199(preserved.)N +928(For)X +1059(example,)X +1371(when)X +1565(the)X +1683(password)X +2006(\256le)X +2128(is)X +2201(updated,)X +2495(\256le)X +2617(locking)X +2877(is)X +2950(used)X +3117(to)X +3199(disallow)X +3490(concurrent)X +3854(access.)X +4120(Tran-)X +555 5289(saction)N +804(semantics)X +1142(on)X +1244(the)X +1364(password)X +1689(\256les)X +1844(would)X +2066(allow)X +2266(concurrent)X +2632(updates,)X +2919(while)X +3119(preserving)X +3479(the)X +3598(logical)X +3837(consistency)X +4232(of)X +555 5379(the)N +681(password)X +1012(database.)X +1357(Similarly,)X +1702(UNIX)X +1930(utilities)X +2196(which)X +2419(rewrite)X +2674(\256les)X +2834(face)X +2996(a)X +3059(potential)X +3366(race)X +3528(condition)X +3857(between)X +4152(their)X +555 5469(rewriting)N +871(a)X +929(\256le)X +1053(and)X +1191(another)X +1453(process)X +1715(reading)X +1977(the)X +2096(\256le.)X +2259(For)X +2391(example,)X +2704(the)X +2823(compiler)X +3129(\(more)X +3342(precisely,)X +3673(the)X +3792(assembler\))X +4161(may)X +8 s +10 f +555 5541(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)N +5 s +1 f +727 5619(1)N +8 s +763 5644(To)N +850(appear)X +1035(in)X +1101(the)X +2 f +1195(Proceedings)X +1530(of)X +1596(the)X +1690(1992)X +1834(Winter)X +2024(Usenix)X +1 f +2201(,)X +2233(San)X +2345(Francisco,)X +2625(CA,)X +2746(January)X +2960(1992.)X + +2 p +%%Page: 2 2 +8 s 8 xH 0 xS 1 f +10 s +3 f +1 f +555 630(have)N +737(to)X +829(rewrite)X +1087(a)X +1152(\256le)X +1283(to)X +1374(which)X +1599(it)X +1672(has)X +1808(write)X +2002(permission)X +2382(in)X +2473(a)X +2538(directory)X +2857(to)X +2948(which)X +3173(it)X +3246(does)X +3422(not)X +3553(have)X +3734(write)X +3928(permission.)X +555 720(While)N +779(the)X +904(``.o'')X +1099(\256le)X +1228(is)X +1308(being)X +1513(written,)X +1787(another)X +2055(utility)X +2272(such)X +2446(as)X +3 f +2540(nm)X +1 f +2651(\(1\))X +2772(or)X +3 f +2866(ar)X +1 f +2942(\(1\))X +3063(may)X +3228(read)X +3394(the)X +3519(\256le)X +3648(and)X +3791(produce)X +4077(invalid)X +555 810(results)N +790(since)X +981(the)X +1105(\256le)X +1233(has)X +1366(not)X +1494(been)X +1672(completely)X +2054(written.)X +2347(Currently,)X +2700(some)X +2895(utilities)X +3160(use)X +3293(special)X +3542(purpose)X +3821(code)X +3998(to)X +4085(handle)X +555 900(such)N +722(cases)X +912(while)X +1110(others)X +1326(ignore)X +1551(the)X +1669(problem)X +1956(and)X +2092(force)X +2278(users)X +2463(to)X +2545(live)X +2685(with)X +2847(the)X +2965(consequences.)X +755 1023(In)N +845(this)X +983(paper,)X +1205(we)X +1322(present)X +1577(a)X +1635(simple)X +1870(library)X +2106(which)X +2324(provides)X +2622(transaction)X +2996(semantics)X +3334(\(atomicity,)X +3705(consistency,)X +4121(isola-)X +555 1113(tion,)N +720(and)X +857(durability\).)X +1236(The)X +1382(4.4BSD)X +1658(database)X +1956(access)X +2182(methods)X +2473(have)X +2645(been)X +2817(modi\256ed)X +3121(to)X +3203(use)X +3330(this)X +3465(library,)X +3719(optionally)X +4063(provid-)X +555 1203(ing)N +682(shared)X +917(buffer)X +1139(management)X +1574(between)X +1867(applications,)X +2298(locking,)X +2582(and)X +2722(transaction)X +3098(semantics.)X +3478(Any)X +3640(UNIX)X +3865(program)X +4161(may)X +555 1293(transaction)N +930(protect)X +1176(its)X +1274(data)X +1430(by)X +1532(requesting)X +1888(transaction)X +2262(protection)X +2609(with)X +2773(the)X +3 f +2893(db)X +1 f +2981(\(3\))X +3097(library)X +3333(or)X +3422(by)X +3524(adding)X +3764(appropriate)X +4152(calls)X +555 1383(to)N +646(the)X +773(transaction)X +1154(manager,)X +1480(buffer)X +1706(manager,)X +2032(lock)X +2199(manager,)X +2525(and)X +2670(log)X +2801(manager.)X +3147(The)X +3301(library)X +3543(routines)X +3829(may)X +3995(be)X +4099(linked)X +555 1473(into)N +708(the)X +834(host)X +995(application)X +1379(and)X +1523(called)X +1743(by)X +1851(subroutine)X +2217(interface,)X +2547(or)X +2642(they)X +2808(may)X +2974(reside)X +3194(in)X +3284(a)X +3348(separate)X +3640(server)X +3865(process.)X +4174(The)X +555 1563(server)N +772(architecture)X +1172(provides)X +1468(for)X +1582(network)X +1865(access)X +2091(and)X +2227(better)X +2430(protection)X +2775(mechanisms.)X +3 f +555 1749(2.)N +655(Related)X +938(Work)X +1 f +755 1872(There)N +1000(has)X +1164(been)X +1373(much)X +1608(discussion)X +1998(in)X +2117(recent)X +2371(years)X +2597(about)X +2831(new)X +3021(transaction)X +3429(models)X +3716(and)X +3888(architectures)X +555 1962 0.1172([SPEC88][NODI90][CHEN91][MOHA91].)AN +2009(Much)X +2220(of)X +2310(this)X +2448(work)X +2636(focuses)X +2900(on)X +3003(new)X +3160(ways)X +3348(to)X +3433(model)X +3656(transactions)X +4062(and)X +4201(the)X +555 2052(interactions)N +953(between)X +1245(them,)X +1449(while)X +1651(the)X +1772(work)X +1960(presented)X +2291(here)X +2453(focuses)X +2717(on)X +2820(the)X +2941(implementation)X +3466(and)X +3605(performance)X +4035(of)X +4125(tradi-)X +555 2142(tional)N +757(transaction)X +1129(techniques)X +1492(\(write-ahead)X +1919(logging)X +2183(and)X +2319(two-phase)X +2669(locking\))X +2956(on)X +3056(a)X +3112(standard)X +3404(operating)X +3727(system)X +3969(\(UNIX\).)X +755 2265(Such)N +947(traditional)X +1308(operating)X +1643(systems)X +1928(are)X +2059(often)X +2256(criticized)X +2587(for)X +2713(their)X +2892(inability)X +3190(to)X +3283(perform)X +3573(transaction)X +3956(processing)X +555 2355(adequately.)N +971([STON81])X +1342(cites)X +1517(three)X +1706(main)X +1894(areas)X +2088(of)X +2183(inadequate)X +2559(support:)X +2849(buffer)X +3074(management,)X +3532(the)X +3658(\256le)X +3788(system,)X +4058(and)X +4201(the)X +555 2445(process)N +823(structure.)X +1191(These)X +1410(arguments)X +1771(are)X +1897(summarized)X +2316(in)X +2405(table)X +2587(one.)X +2769(Fortunately,)X +3184(much)X +3388(has)X +3521(changed)X +3815(since)X +4006(1981.)X +4232(In)X +555 2535(the)N +683(area)X +848(of)X +945(buffer)X +1172(management,)X +1632(most)X +1817(UNIX)X +2048(systems)X +2331(provide)X +2606(the)X +2734(ability)X +2968(to)X +3060(memory)X +3357(map)X +3525(\256les,)X +3708(thus)X +3870(obviating)X +4201(the)X +555 2625(need)N +734(for)X +855(a)X +918(copy)X +1101(between)X +1396(kernel)X +1624(and)X +1766(user)X +1926(space.)X +2171(If)X +2251(a)X +2313(database)X +2616(system)X +2864(is)X +2943(going)X +3151(to)X +3239(use)X +3372(the)X +3496(\256le)X +3624(system)X +3872(buffer)X +4095(cache,)X +555 2715(then)N +719(a)X +781(system)X +1029(call)X +1171(is)X +1250(required.)X +1584(However,)X +1924(if)X +1998(buffering)X +2322(is)X +2400(provided)X +2710(at)X +2793(user)X +2952(level)X +3133(using)X +3331(shared)X +3566(memory,)X +3878(as)X +3970(in)X +4057(LIBTP,)X +555 2805(buffer)N +776(management)X +1210(is)X +1287(only)X +1452(as)X +1542(slow)X +1716(as)X +1806(access)X +2035(to)X +2120(shared)X +2353(memory)X +2643(and)X +2782(any)X +2921(replacement)X +3337(algorithm)X +3671(may)X +3832(be)X +3931(used.)X +4121(Since)X +555 2895(multiple)N +849(processes)X +1185(can)X +1325(access)X +1559(the)X +1685(shared)X +1923(data,)X +2105(prefetching)X +2499(may)X +2665(be)X +2769(accomplished)X +3238(by)X +3346(separate)X +3638(processes)X +3973(or)X +4067(threads)X +555 2985(whose)N +782(sole)X +932(purpose)X +1207(is)X +1281(to)X +1364(prefetch)X +1649(pages)X +1853(and)X +1990(wait)X +2149(on)X +2250(them.)X +2471(There)X +2680(is)X +2754(still)X +2894(no)X +2995(way)X +3150(to)X +3233(enforce)X +3496(write)X +3682(ordering)X +3975(other)X +4161(than)X +555 3075(keeping)N +829(pages)X +1032(in)X +1114(user)X +1268(memory)X +1555(and)X +1691(using)X +1884(the)X +3 f +2002(fsync)X +1 f +2180(\(3\))X +2294(system)X +2536(call)X +2672(to)X +2754(perform)X +3033(synchronous)X +3458(writes.)X +755 3198(In)N +845(the)X +966(area)X +1124(of)X +1214(\256le)X +1339(systems,)X +1635(the)X +1756(fast)X +1895(\256le)X +2020(system)X +2265(\(FFS\))X +2474([MCKU84])X +2871(allows)X +3103(allocation)X +3442(in)X +3527(units)X +3704(up)X +3806(to)X +3890(64KBytes)X +4232(as)X +555 3288(opposed)N +846(to)X +932(the)X +1054(4KByte)X +1327(and)X +1466(8KByte)X +1738(\256gures)X +1979(quoted)X +2220(in)X +2305([STON81].)X +2711(The)X +2859(measurements)X +3341(in)X +3426(this)X +3564(paper)X +3766(were)X +3946(taken)X +4143(from)X +555 3378(an)N +655(8KByte)X +928(FFS,)X +1104(but)X +1230(as)X +1320(LIBTP)X +1565(runs)X +1726(exclusively)X +2114(in)X +2199(user)X +2356(space,)X +2578(there)X +2762(is)X +2838(nothing)X +3105(to)X +3190(prevent)X +3454(it)X +3521(from)X +3700(being)X +3901(run)X +4031(on)X +4134(other)X +555 3468(UNIX)N +776(compatible)X +1152(\256le)X +1274(systems)X +1547(\(e.g.)X +1710(log-structured)X +2180([ROSE91],)X +2558(extent-based,)X +3004(or)X +3091(multi-block)X +3484([SELT91]\).)X +755 3591(Finally,)N +1029(with)X +1199(regard)X +1433(to)X +1523(the)X +1648(process)X +1916(structure,)X +2244(neither)X +2494(context)X +2757(switch)X +2993(time)X +3162(nor)X +3296(scheduling)X +3670(around)X +3920(semaphores)X +555 3681(seems)N +785(to)X +881(affect)X +1099(the)X +1231(system)X +1487(performance.)X +1968(However,)X +2317(the)X +2449(implementation)X +2984(of)X +3084(semaphores)X +3496(can)X +3641(impact)X +3892(performance)X +555 3771(tremendously.)N +1051(This)X +1213(is)X +1286(discussed)X +1613(in)X +1695(more)X +1880(detail)X +2078(in)X +2160(section)X +2407(4.3.)X +755 3894(The)N +908(Tuxedo)X +1181(system)X +1431(from)X +1615(AT&T)X +1861(is)X +1941(a)X +2004(transaction)X +2383(manager)X +2687(which)X +2910(coordinates)X +3307(distributed)X +3676(transaction)X +4055(commit)X +555 3984(from)N +738(a)X +801(variety)X +1051(of)X +1145(different)X +1449(local)X +1632(transaction)X +2011(managers.)X +2386(At)X +2493(this)X +2634(time,)X +2822(LIBTP)X +3070(does)X +3243(not)X +3371(have)X +3549(its)X +3650(own)X +3814(mechanism)X +4205(for)X +555 4074(distributed)N +942(commit)X +1231(processing,)X +1639(but)X +1786(could)X +2009(be)X +2130(used)X +2322(as)X +2434(a)X +2515(local)X +2716(transaction)X +3113(agent)X +3331(by)X +3455(systems)X +3752(such)X +3943(as)X +4054(Tuxedo)X +555 4164([ANDR89].)N +10 f +863 4393(i)N +870(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +903 4483(Buffer)N +1133(Management)X +10 f +1672(g)X +1 f +1720(Data)X +1892(must)X +2067(be)X +2163(copied)X +2397(between)X +2685(kernel)X +2906(space)X +3105(and)X +3241(user)X +3395(space.)X +10 f +1672 4573(g)N +1 f +1720(Buffer)X +1950(pool)X +2112(access)X +2338(is)X +2411(too)X +2533(slow.)X +10 f +1672 4663(g)N +1 f +1720(There)X +1928(is)X +2001(no)X +2101(way)X +2255(to)X +2337(request)X +2589(prefetch.)X +10 f +1672 4753(g)N +1 f +1720(Replacement)X +2159(is)X +2232(usually)X +2483(LRU)X +2663(which)X +2879(may)X +3037(be)X +3133(suboptimal)X +3508(for)X +3622(databases.)X +10 f +1672 4843(g)N +1 f +1720(There)X +1928(is)X +2001(no)X +2101(way)X +2255(to)X +2337(guarantee)X +2670(write)X +2855(ordering.)X +10 f +863 4853(i)N +870(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +903 4943(File)N +1047(System)X +10 f +1672(g)X +1 f +1720(Allocation)X +2078(is)X +2151(done)X +2327(in)X +2409(small)X +2602(blocks)X +2831(\(usually)X +3109(4K)X +3227(or)X +3314(8K\).)X +10 f +1672 5033(g)N +1 f +1720(Logical)X +1985(organization)X +2406(of)X +2493(\256les)X +2646(is)X +2719(redundantly)X +3122(expressed.)X +10 f +863 5043(i)N +870(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +903 5133(Process)N +1168(Structure)X +10 f +1672(g)X +1 f +1720(Context)X +1993(switching)X +2324(and)X +2460(message)X +2752(passing)X +3012(are)X +3131(too)X +3253(slow.)X +10 f +1672 5223(g)N +1 f +1720(A)X +1798(process)X +2059(may)X +2217(be)X +2313(descheduled)X +2730(while)X +2928(holding)X +3192(a)X +3248(semaphore.)X +10 f +863 5233(i)N +870(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +863(c)X +5193(c)Y +5113(c)Y +5033(c)Y +4953(c)Y +4873(c)Y +4793(c)Y +4713(c)Y +4633(c)Y +4553(c)Y +4473(c)Y +3990 5233(c)N +5193(c)Y +5113(c)Y +5033(c)Y +4953(c)Y +4873(c)Y +4793(c)Y +4713(c)Y +4633(c)Y +4553(c)Y +4473(c)Y +3 f +1156 5446(Table)N +1371(One:)X +1560(Shortcomings)X +2051(of)X +2138(UNIX)X +2363(transaction)X +2770(support)X +3056(cited)X +3241(in)X +3327([STON81].)X + +3 p +%%Page: 3 3 +10 s 10 xH 0 xS 3 f +1 f +755 630(The)N +901(transaction)X +1274(architecture)X +1675(presented)X +2004(in)X +2087([YOUN91])X +2474(is)X +2548(very)X +2712(similar)X +2955(to)X +3038(that)X +3179(implemented)X +3618(in)X +3701(the)X +3820(LIBTP.)X +4103(While)X +555 720([YOUN91])N +947(presents)X +1236(a)X +1298(model)X +1524(for)X +1644(providing)X +1981(transaction)X +2359(services,)X +2663(this)X +2803(paper)X +3007(focuses)X +3273(on)X +3378(the)X +3501(implementation)X +4028(and)X +4169(per-)X +555 810(formance)N +881(of)X +970(a)X +1028(particular)X +1358(system.)X +1642(In)X +1731(addition,)X +2034(we)X +2149(provide)X +2415(detailed)X +2690(comparisons)X +3116(with)X +3279(alternative)X +3639(solutions:)X +3970(traditional)X +555 900(UNIX)N +776(services)X +1055(and)X +1191(commercial)X +1590(database)X +1887(management)X +2317(systems.)X +3 f +555 1086(3.)N +655(Architecture)X +1 f +755 1209(The)N +906(library)X +1146(is)X +1224(designed)X +1534(to)X +1621(provide)X +1891(well)X +2054(de\256ned)X +2315(interfaces)X +2653(to)X +2740(the)X +2863(services)X +3147(required)X +3440(for)X +3559(transaction)X +3936(processing.)X +555 1299(These)N +777(services)X +1066(are)X +1195(recovery,)X +1527(concurrency)X +1955(control,)X +2232(and)X +2378(the)X +2506(management)X +2946(of)X +3043(shared)X +3283(data.)X +3487(First)X +3663(we)X +3787(will)X +3941(discuss)X +4201(the)X +555 1389(design)N +795(tradeoffs)X +1112(in)X +1205(the)X +1334(selection)X +1650(of)X +1748(recovery,)X +2081(concurrency)X +2510(control,)X +2787(and)X +2933(buffer)X +3160(management)X +3600(implementations,)X +4183(and)X +555 1479(then)N +713(we)X +827(will)X +971(present)X +1223(the)X +1341(overall)X +1584(library)X +1818(architecture)X +2218(and)X +2354(module)X +2614(descriptions.)X +3 f +555 1665(3.1.)N +715(Design)X +966(Tradeoffs)X +1 f +3 f +555 1851(3.1.1.)N +775(Crash)X +1004(Recovery)X +1 f +755 1974(The)N +909(recovery)X +1220(protocol)X +1516(is)X +1598(responsible)X +1992(for)X +2115(providing)X +2455(the)X +2582(transaction)X +2963(semantics)X +3308(discussed)X +3644(earlier.)X +3919(There)X +4136(are)X +4263(a)X +555 2064(wide)N +739(range)X +946(of)X +1041(recovery)X +1351(protocols)X +1677(available)X +1995([HAER83],)X +2395(but)X +2525(we)X +2647(can)X +2786(crudely)X +3054(divide)X +3281(them)X +3468(into)X +3619(two)X +3766(main)X +3953(categories.)X +555 2154(The)N +706(\256rst)X +856(category)X +1159(records)X +1422(all)X +1528(modi\256cations)X +1989(to)X +2077(the)X +2201(database)X +2504(in)X +2592(a)X +2653(separate)X +2942(\256le,)X +3089(and)X +3230(uses)X +3393(this)X +3533(\256le)X +3660(\(log\))X +3841(to)X +3928(back)X +4105(out)X +4232(or)X +555 2244(reapply)N +825(these)X +1019(modi\256cations)X +1483(if)X +1561(a)X +1626(transaction)X +2007(aborts)X +2232(or)X +2328(the)X +2455(system)X +2706(crashes.)X +3012(We)X +3153(call)X +3298(this)X +3442(set)X +3560(the)X +3 f +3687(logging)X +3963(protocols)X +1 f +4279(.)X +555 2334(The)N +703(second)X +949(category)X +1249(avoids)X +1481(the)X +1602(use)X +1732(of)X +1822(a)X +1881(log)X +2006(by)X +2109(carefully)X +2418(controlling)X +2792(when)X +2989(data)X +3146(are)X +3268(written)X +3518(to)X +3603(disk.)X +3799(We)X +3934(call)X +4073(this)X +4210(set)X +555 2424(the)N +3 f +673(non-logging)X +1096(protocols)X +1 f +1412(.)X +755 2547(Non-logging)N +1185(protocols)X +1504(hold)X +1666(dirty)X +1837(buffers)X +2085(in)X +2167(main)X +2347(memory)X +2634(or)X +2721(temporary)X +3071(\256les)X +3224(until)X +3390(commit)X +3654(and)X +3790(then)X +3948(force)X +4134(these)X +555 2637(pages)N +769(to)X +862(disk)X +1026(at)X +1115(transaction)X +1498(commit.)X +1813(While)X +2040(we)X +2165(can)X +2308(use)X +2446(temporary)X +2807(\256les)X +2971(to)X +3064(hold)X +3237(dirty)X +3418(pages)X +3631(that)X +3781(may)X +3949(need)X +4131(to)X +4223(be)X +555 2727(evicted)N +810(from)X +988(memory)X +1277(during)X +1508(a)X +1566(long-running)X +2006(transaction,)X +2400(the)X +2520(only)X +2684(user-level)X +3023(mechanism)X +3410(to)X +3494(force)X +3682(pages)X +3887(to)X +3971(disk)X +4126(is)X +4201(the)X +3 f +555 2817(fsync)N +1 f +733(\(2\))X +850(system)X +1095(call.)X +1274(Unfortunately,)X +3 f +1767(fsync)X +1 f +1945(\(2\))X +2062(is)X +2138(an)X +2237(expensive)X +2581(system)X +2826(call)X +2965(in)X +3050(that)X +3193(it)X +3260(forces)X +3480(all)X +3583(pages)X +3789(of)X +3879(a)X +3938(\256le)X +4062(to)X +4146(disk,)X +555 2907(and)N +691(transactions)X +1094(that)X +1234(manage)X +1504(more)X +1689(than)X +1847(one)X +1983(\256le)X +2105(must)X +2280(issue)X +2460(one)X +2596(call)X +2732(per)X +2855(\256le.)X +755 3030(In)N +853(addition,)X +3 f +1166(fsync)X +1 f +1344(\(2\))X +1469(provides)X +1776(no)X +1887(way)X +2051(to)X +2143(control)X +2400(the)X +2528(order)X +2728(in)X +2820(which)X +3046(dirty)X +3227(pages)X +3440(are)X +3569(written)X +3826(to)X +3918(disk.)X +4121(Since)X +555 3120(non-logging)N +976(protocols)X +1304(must)X +1489(sometimes)X +1861(order)X +2061(writes)X +2287(carefully)X +2603([SULL92],)X +2987(they)X +3155(are)X +3284(dif\256cult)X +3567(to)X +3659(implement)X +4030(on)X +4139(Unix)X +555 3210(systems.)N +868(As)X +977(a)X +1033(result,)X +1251(we)X +1365(have)X +1537(chosen)X +1780(to)X +1862(implement)X +2224(a)X +2280(logging)X +2544(protocol.)X +755 3333(Logging)N +1050(protocols)X +1372(may)X +1534(be)X +1634(categorized)X +2029(based)X +2236(on)X +2340(how)X +2502(information)X +2904(is)X +2981(logged)X +3223(\(physically)X +3602(or)X +3692(logically\))X +4022(and)X +4161(how)X +555 3423(much)N +767(is)X +854(logged)X +1106(\(before)X +1373(images,)X +1654(after)X +1836(images)X +2097(or)X +2198(both\).)X +2441(In)X +3 f +2542(physical)X +2855(logging)X +1 f +3103(,)X +3157(images)X +3417(of)X +3517(complete)X +3844(physical)X +4144(units)X +555 3513(\(pages)N +786(or)X +874(buffers\))X +1150(are)X +1270(recorded,)X +1593(while)X +1792(in)X +3 f +1875(logical)X +2118(logging)X +1 f +2387(a)X +2444(description)X +2820(of)X +2907(the)X +3025(operation)X +3348(is)X +3421(recorded.)X +3763(Therefore,)X +4121(while)X +555 3603(we)N +675(may)X +839(record)X +1071(entire)X +1280(pages)X +1489(in)X +1577(a)X +1639(physical)X +1932(log,)X +2080(we)X +2200(need)X +2378(only)X +2546(record)X +2777(the)X +2900(records)X +3162(being)X +3365(modi\256ed)X +3674(in)X +3761(a)X +3822(logical)X +4065(log.)X +4232(In)X +555 3693(fact,)N +718(physical)X +1006(logging)X +1271(can)X +1404(be)X +1501(thought)X +1766(of)X +1854(as)X +1942(a)X +1999(special)X +2243(case)X +2403(of)X +2491(logical)X +2730(logging,)X +3015(since)X +3201(the)X +3320 0.3125(``records'')AX +3686(that)X +3827(we)X +3942(log)X +4065(in)X +4148(logi-)X +555 3783(cal)N +673(logging)X +941(might)X +1151(be)X +1251(physical)X +1542(pages.)X +1789(Since)X +1991(logical)X +2233(logging)X +2501(is)X +2578(both)X +2743(more)X +2931(space-ef\256cient)X +3423(and)X +3562(more)X +3750(general,)X +4030(we)X +4147(have)X +555 3873(chosen)N +798(it)X +862(for)X +976(our)X +1103(logging)X +1367(protocol.)X +755 3996(In)N +3 f +843(before-image)X +1315(logging)X +1 f +1563(,)X +1604(we)X +1719(log)X +1842(a)X +1899(copy)X +2076(of)X +2164(the)X +2283(data)X +2438(before)X +2665(the)X +2784(update,)X +3039(while)X +3238(in)X +3 f +3321(after-image)X +3739(logging)X +1 f +3987(,)X +4027(we)X +4141(log)X +4263(a)X +555 4086(copy)N +740(of)X +836(the)X +963(data)X +1126(after)X +1303(the)X +1429(update.)X +1711(If)X +1793(we)X +1915(log)X +2045(only)X +2215(before-images,)X +2723(then)X +2889(there)X +3078(is)X +3159(suf\256cient)X +3485(information)X +3891(in)X +3981(the)X +4107(log)X +4237(to)X +555 4176(allow)N +761(us)X +860(to)X +3 f +950(undo)X +1 f +1150(the)X +1276(transaction)X +1656(\(go)X +1791(back)X +1971(to)X +2061(the)X +2187(state)X +2361(represented)X +2759(by)X +2866(the)X +2991(before-image\).)X +3514(However,)X +3876(if)X +3952(the)X +4077(system)X +555 4266(crashes)N +814(and)X +952(a)X +1010(committed)X +1374(transaction's)X +1806(changes)X +2087(have)X +2261(not)X +2385(reached)X +2658(the)X +2778(disk,)X +2953(we)X +3068(have)X +3241(no)X +3342(means)X +3568(to)X +3 f +3651(redo)X +1 f +3828(the)X +3947(transaction)X +555 4356(\(reapply)N +849(the)X +973(updates\).)X +1311(Therefore,)X +1675(logging)X +1945(only)X +2113(before-images)X +2599(necessitates)X +3004(forcing)X +3262(dirty)X +3439(pages)X +3648(at)X +3732(commit)X +4002(time.)X +4210(As)X +555 4446(mentioned)N +913(above,)X +1145(forcing)X +1397(pages)X +1600(at)X +1678(commit)X +1942(is)X +2015(considered)X +2383(too)X +2505(costly.)X +755 4569(If)N +834(we)X +953(log)X +1080(only)X +1247(after-images,)X +1694(then)X +1857(there)X +2043(is)X +2121(suf\256cient)X +2444(information)X +2847(in)X +2934(the)X +3057(log)X +3184(to)X +3271(allow)X +3474(us)X +3570(to)X +3657(redo)X +3825(the)X +3947(transaction)X +555 4659(\(go)N +687(forward)X +967(to)X +1054(the)X +1177(state)X +1348(represented)X +1743(by)X +1847(the)X +1969(after-image\),)X +2411(but)X +2537(we)X +2655(do)X +2759(not)X +2885(have)X +3061(the)X +3183(information)X +3585(required)X +3877(to)X +3963(undo)X +4147(tran-)X +555 4749(sactions)N +845(which)X +1073(aborted)X +1346(after)X +1526(dirty)X +1709(pages)X +1924(were)X +2113(written)X +2372(to)X +2466(disk.)X +2670(Therefore,)X +3039(logging)X +3314(only)X +3487(after-images)X +3920(necessitates)X +555 4839(holding)N +819(all)X +919(dirty)X +1090(buffers)X +1338(in)X +1420(main)X +1600(memory)X +1887(until)X +2053(commit)X +2317(or)X +2404(writing)X +2655(them)X +2835(to)X +2917(a)X +2973(temporary)X +3323(\256le.)X +755 4962(Since)N +956(neither)X +1202(constraint)X +1541(\(forcing)X +1823(pages)X +2029(on)X +2132(commit)X +2399(or)X +2489(buffering)X +2811(pages)X +3016(until)X +3184(commit\))X +3477(was)X +3624(feasible,)X +3916(we)X +4032(chose)X +4237(to)X +555 5052(log)N +683(both)X +851(before)X +1083(and)X +1225(after)X +1399(images.)X +1672(The)X +1823(only)X +1991(remaining)X +2342(consideration)X +2800(is)X +2879(when)X +3079(changes)X +3363(get)X +3486(written)X +3738(to)X +3825(disk.)X +4023(Changes)X +555 5142(affect)N +764(both)X +931(data)X +1090(pages)X +1298(and)X +1438(the)X +1560(log.)X +1726(If)X +1804(the)X +1926(changed)X +2218(data)X +2376(page)X +2552(is)X +2629(written)X +2880(before)X +3110(the)X +3232(log)X +3358(page,)X +3554(and)X +3694(the)X +3816(system)X +4062(crashes)X +555 5232(before)N +787(the)X +911(log)X +1039(page)X +1217(is)X +1296(written,)X +1569(the)X +1693(log)X +1820(will)X +1969(contain)X +2230(insuf\256cient)X +2615(information)X +3018(to)X +3105(undo)X +3290(the)X +3413(change.)X +3706(This)X +3873(violates)X +4147(tran-)X +555 5322(saction)N +803(semantics,)X +1160(since)X +1346(some)X +1536(changed)X +1825(data)X +1980(pages)X +2184(may)X +2343(not)X +2466(have)X +2638(been)X +2810(written,)X +3077(and)X +3213(the)X +3331(database)X +3628(cannot)X +3862(be)X +3958(restored)X +4237(to)X +555 5412(its)N +650(pre-transaction)X +1152(state.)X +755 5535(The)N +914(log)X +1050(record)X +1290(describing)X +1658(an)X +1768(update)X +2016(must)X +2205(be)X +2315(written)X +2576(to)X +2672(stable)X +2893(storage)X +3159(before)X +3398(the)X +3529(modi\256ed)X +3846(page.)X +4071(This)X +4246(is)X +3 f +555 5625(write-ahead)N +992(logging)X +1 f +1240(.)X +1307(If)X +1388(log)X +1517(records)X +1781(are)X +1907(safely)X +2126(written)X +2380(to)X +2469(disk,)X +2649(data)X +2810(pages)X +3020(may)X +3185(be)X +3288(written)X +3542(at)X +3627(any)X +3770(time)X +3939(afterwards.)X +555 5715(This)N +721(means)X +950(that)X +1094(the)X +1216(only)X +1382(\256le)X +1508(that)X +1652(ever)X +1815(needs)X +2022(to)X +2108(be)X +2208(forced)X +2438(to)X +2524(disk)X +2681(is)X +2758(the)X +2880(log.)X +3046(Since)X +3248(the)X +3370(log)X +3495(is)X +3571(append-only,)X +4015(modi\256ed)X + +4 p +%%Page: 4 4 +10 s 10 xH 0 xS 1 f +3 f +1 f +555 630(pages)N +760(always)X +1005(appear)X +1242(at)X +1322(the)X +1442(end)X +1580(and)X +1718(may)X +1878(be)X +1976(written)X +2224(to)X +2307(disk)X +2461(ef\256ciently)X +2807(in)X +2890(any)X +3027(\256le)X +3150(system)X +3393(that)X +3534(favors)X +3756(sequential)X +4102(order-)X +555 720(ing)N +677(\()X +2 f +704(e.g.)X +1 f +820(,)X +860(FFS,)X +1032(log-structured)X +1502(\256le)X +1624(system,)X +1886(or)X +1973(an)X +2069(extent-based)X +2495(system\).)X +3 f +555 906(3.1.2.)N +775(Concurrency)X +1245(Control)X +1 f +755 1029(The)N +918(concurrency)X +1354(control)X +1619(protocol)X +1923(is)X +2013(responsible)X +2415(for)X +2546(maintaining)X +2965(consistency)X +3376(in)X +3475(the)X +3610(presence)X +3929(of)X +4033(multiple)X +555 1119(accesses.)N +897(There)X +1114(are)X +1242(several)X +1499(alternative)X +1867(solutions)X +2183(such)X +2358(as)X +2453(locking,)X +2741(optimistic)X +3088(concurrency)X +3514(control)X +3769([KUNG81],)X +4183(and)X +555 1209(timestamp)N +912(ordering)X +1208([BERN80].)X +1619(Since)X +1821(optimistic)X +2164(methods)X +2459(and)X +2599(timestamp)X +2956(ordering)X +3252(are)X +3374(generally)X +3696(more)X +3884(complex)X +4183(and)X +555 1299(restrict)N +804(concurrency)X +1228(without)X +1498(eliminating)X +1888(starvation)X +2230(or)X +2323(deadlocks,)X +2690(we)X +2810(chose)X +3018(two-phase)X +3373(locking)X +3638(\(2PL\).)X +3890(Strict)X +4088(2PL)X +4246(is)X +555 1389(suboptimal)N +935(for)X +1054(certain)X +1297(data)X +1455(structures)X +1791(such)X +1962(as)X +2053(B-trees)X +2309(because)X +2588(it)X +2656(can)X +2792(limit)X +2966(concurrency,)X +3408(so)X +3503(we)X +3621(use)X +3752(a)X +3812(special)X +4059(locking)X +555 1479(protocol)N +842(based)X +1045(on)X +1145(one)X +1281(described)X +1609(in)X +1691([LEHM81].)X +755 1602(The)N +901(B-tree)X +1123(locking)X +1384(protocol)X +1672(we)X +1787(implemented)X +2226(releases)X +2502(locks)X +2691(at)X +2769(internal)X +3034(nodes)X +3241(in)X +3323(the)X +3441(tree)X +3582(as)X +3669(it)X +3733(descends.)X +4083(A)X +4161(lock)X +555 1692(on)N +658(an)X +757(internal)X +1025(page)X +1200(is)X +1276(always)X +1522(released)X +1808(before)X +2036(a)X +2094(lock)X +2254(on)X +2356(its)X +2453(child)X +2635(is)X +2710(obtained)X +3008(\(that)X +3177(is,)X +3272(locks)X +3463(are)X +3584(not)X +3 f +3708(coupled)X +1 f +3996([BAY77])X +555 1782(during)N +786(descent\).)X +1116(When)X +1330(a)X +1388(leaf)X +1531(\(or)X +1647(internal\))X +1941(page)X +2115(is)X +2190(split,)X +2369(a)X +2427(write)X +2614(lock)X +2774(is)X +2849(acquired)X +3148(on)X +3250(the)X +3370(parent)X +3593(before)X +3821(the)X +3941(lock)X +4100(on)X +4201(the)X +555 1872(just-split)N +855(page)X +1028(is)X +1102(released)X +1387(\(locks)X +1604(are)X +3 f +1724(coupled)X +1 f +2011(during)X +2241(ascent\).)X +2530(Write)X +2734(locks)X +2924(on)X +3025(internal)X +3291(pages)X +3495(are)X +3615(released)X +3899(immediately)X +555 1962(after)N +723(the)X +841(page)X +1013(is)X +1086(updated,)X +1380(but)X +1502(locks)X +1691(on)X +1791(leaf)X +1932(pages)X +2135(are)X +2254(held)X +2412(until)X +2578(the)X +2696(end)X +2832(of)X +2919(the)X +3037(transaction.)X +755 2085(Since)N +964(locks)X +1164(are)X +1294(released)X +1589(during)X +1828(descent,)X +2119(the)X +2247(structure)X +2558(of)X +2655(the)X +2783(tree)X +2934(may)X +3102(change)X +3360(above)X +3582(a)X +3648(node)X +3834(being)X +4042(used)X +4219(by)X +555 2175(some)N +752(process.)X +1061(If)X +1143(that)X +1291(process)X +1560(must)X +1743(later)X +1914(ascend)X +2161(the)X +2287(tree)X +2435(because)X +2717(of)X +2811(a)X +2874(page)X +3053(split,)X +3237(any)X +3380(such)X +3554(change)X +3809(must)X +3991(not)X +4120(cause)X +555 2265(confusion.)N +938(We)X +1077(use)X +1211(the)X +1336(technique)X +1675(described)X +2010(in)X +2099([LEHM81])X +2487(which)X +2710(exploits)X +2989(the)X +3113(ordering)X +3411(of)X +3504(data)X +3664(on)X +3770(a)X +3832(B-tree)X +4059(page)X +4237(to)X +555 2355(guarantee)N +888(that)X +1028(no)X +1128(process)X +1389(ever)X +1548(gets)X +1697(lost)X +1832(as)X +1919(a)X +1975(result)X +2173(of)X +2260(internal)X +2525(page)X +2697(updates)X +2962(made)X +3156(by)X +3256(other)X +3441(processes.)X +755 2478(If)N +836(a)X +899(transaction)X +1278(that)X +1425(updates)X +1697(a)X +1760(B-tree)X +1988(aborts,)X +2231(the)X +2356(user-visible)X +2757(changes)X +3043(to)X +3131(the)X +3255(tree)X +3402(must)X +3583(be)X +3685(rolled)X +3898(back.)X +4116(How-)X +555 2568(ever,)N +735(changes)X +1015(to)X +1097(the)X +1215(internal)X +1480(nodes)X +1687(of)X +1774(the)X +1892(tree)X +2033(need)X +2205(not)X +2327(be)X +2423(rolled)X +2630(back,)X +2822(since)X +3007(these)X +3192(pages)X +3395(contain)X +3651(no)X +3751(user-visible)X +4145(data.)X +555 2658(When)N +771(rolling)X +1008(back)X +1184(a)X +1244(transaction,)X +1640(we)X +1758(roll)X +1893(back)X +2069(all)X +2173(leaf)X +2318(page)X +2494(updates,)X +2783(but)X +2909(no)X +3013(internal)X +3281(insertions)X +3615(or)X +3705(page)X +3880(splits.)X +4111(In)X +4201(the)X +555 2748(worst)N +759(case,)X +944(this)X +1085(will)X +1235(leave)X +1431(a)X +1493(leaf)X +1640(page)X +1818(less)X +1964(than)X +2128(half)X +2279(full.)X +2456(This)X +2624(may)X +2788(cause)X +2993(poor)X +3166(space)X +3371(utilization,)X +3741(but)X +3869(does)X +4042(not)X +4170(lose)X +555 2838(user)N +709(data.)X +755 2961(Holding)N +1038(locks)X +1228(on)X +1329(leaf)X +1471(pages)X +1675(until)X +1842(transaction)X +2215(commit)X +2480(guarantees)X +2845(that)X +2986(no)X +3087(other)X +3273(process)X +3535(can)X +3668(insert)X +3866(or)X +3953(delete)X +4165(data)X +555 3051(that)N +711(has)X +854(been)X +1042(touched)X +1332(by)X +1448(this)X +1598(process.)X +1914(Rolling)X +2188(back)X +2375(insertions)X +2721(and)X +2872(deletions)X +3196(on)X +3311(leaf)X +3467(pages)X +3685(guarantees)X +4064(that)X +4219(no)X +555 3141(aborted)N +819(updates)X +1087(are)X +1209(ever)X +1371(visible)X +1607(to)X +1692(other)X +1880(transactions.)X +2326(Leaving)X +2612(page)X +2787(splits)X +2978(intact)X +3179(permits)X +3442(us)X +3536(to)X +3621(release)X +3867(internal)X +4134(write)X +555 3231(locks)N +744(early.)X +965(Thus)X +1145(transaction)X +1517(semantics)X +1853(are)X +1972(preserved,)X +2325(and)X +2461(locks)X +2650(are)X +2769(held)X +2927(for)X +3041(shorter)X +3284(periods.)X +755 3354(The)N +901(extra)X +1083(complexity)X +1464(introduced)X +1828(by)X +1929(this)X +2065(locking)X +2326(protocol)X +2614(appears)X +2881(substantial,)X +3264(but)X +3387(it)X +3452(is)X +3525(important)X +3856(for)X +3970(multi-user)X +555 3444(execution.)N +950(The)X +1118(bene\256ts)X +1410(of)X +1520(non-two-phase)X +2040(locking)X +2323(on)X +2446(B-trees)X +2721(are)X +2863(well)X +3044(established)X +3443(in)X +3548(the)X +3689(database)X +4009(literature)X +555 3534([BAY77],)N +899([LEHM81].)X +1320(If)X +1394(a)X +1450(process)X +1711(held)X +1869(locks)X +2058(until)X +2224(it)X +2288(committed,)X +2670(then)X +2828(a)X +2884(long-running)X +3322(update)X +3556(could)X +3754(lock)X +3912(out)X +4034(all)X +4134(other)X +555 3624(transactions)N +967(by)X +1076(preventing)X +1448(any)X +1593(other)X +1787(process)X +2057(from)X +2241(locking)X +2509(the)X +2635(root)X +2792(page)X +2972(of)X +3067(the)X +3193(tree.)X +3382(The)X +3535(B-tree)X +3764(locking)X +4032(protocol)X +555 3714(described)N +884(above)X +1096(guarantees)X +1460(that)X +1600(locks)X +1789(on)X +1889(internal)X +2154(pages)X +2357(are)X +2476(held)X +2634(for)X +2748(extremely)X +3089(short)X +3269(periods,)X +3545(thereby)X +3806(increasing)X +4156(con-)X +555 3804(currency.)N +3 f +555 3990(3.1.3.)N +775(Management)X +1245(of)X +1332(Shared)X +1596(Data)X +1 f +755 4113(Database)N +1075(systems)X +1353(permit)X +1587(many)X +1790(users)X +1980(to)X +2067(examine)X +2364(and)X +2505(update)X +2744(the)X +2866(same)X +3055(data)X +3213(concurrently.)X +3683(In)X +3774(order)X +3968(to)X +4054(provide)X +555 4203(this)N +702(concurrent)X +1078(access)X +1316(and)X +1464(enforce)X +1738(the)X +1868(write-ahead)X +2280(logging)X +2556(protocol)X +2855(described)X +3195(in)X +3289(section)X +3548(3.1.1,)X +3759(we)X +3884(use)X +4022(a)X +4089(shared)X +555 4293(memory)N +848(buffer)X +1071(manager.)X +1414(Not)X +1559(only)X +1726(does)X +1898(this)X +2038(provide)X +2308(the)X +2431(guarantees)X +2800(we)X +2919(require,)X +3192(but)X +3319(a)X +3380(user-level)X +3722(buffer)X +3944(manager)X +4246(is)X +555 4383(frequently)N +916(faster)X +1126(than)X +1295(using)X +1498(the)X +1626(\256le)X +1758(system)X +2010(buffer)X +2237(cache.)X +2491(Reads)X +2717(or)X +2814(writes)X +3040(involving)X +3376(the)X +3504(\256le)X +3636(system)X +3888(buffer)X +4115(cache)X +555 4473(often)N +746(require)X +1000(copying)X +1284(data)X +1444(between)X +1738(user)X +1898(and)X +2040(kernel)X +2266(space)X +2470(while)X +2673(a)X +2734(user-level)X +3076(buffer)X +3298(manager)X +3600(can)X +3737(return)X +3954(pointers)X +4237(to)X +555 4563(data)N +709(pages)X +912(directly.)X +1217(Additionally,)X +1661(if)X +1730(more)X +1915(than)X +2073(one)X +2209(process)X +2470(uses)X +2628(the)X +2746(same)X +2931(page,)X +3123(then)X +3281(fewer)X +3485(copies)X +3710(may)X +3868(be)X +3964(required.)X +3 f +555 4749(3.2.)N +715(Module)X +997(Architecture)X +1 f +755 4872(The)N +913(preceding)X +1262(sections)X +1552(described)X +1892(modules)X +2195(for)X +2321(managing)X +2669(the)X +2799(transaction)X +3183(log,)X +3337(locks,)X +3558(and)X +3706(a)X +3774(cache)X +3990(of)X +4089(shared)X +555 4962(buffers.)N +847(In)X +938(addition,)X +1244(we)X +1362(need)X +1538(to)X +1624(provide)X +1893(functionality)X +2326(for)X +2444(transaction)X +2 f +2819(begin)X +1 f +2997(,)X +2 f +3040(commit)X +1 f +3276(,)X +3319(and)X +2 f +3458(abort)X +1 f +3654(processing,)X +4040(necessi-)X +555 5052(tating)N +769(a)X +837(transaction)X +1221(manager.)X +1570(In)X +1669(order)X +1871(to)X +1965(arbitrate)X +2265(concurrent)X +2641(access)X +2879(to)X +2973(locks)X +3173(and)X +3320(buffers,)X +3599(we)X +3724(include)X +3991(a)X +4058(process)X +555 5142(management)N +995(module)X +1264(which)X +1489(manages)X +1799(a)X +1864(collection)X +2209(of)X +2305(semaphores)X +2713(used)X +2889(to)X +2980(block)X +3187(and)X +3332(release)X +3585(processes.)X +3962(Finally,)X +4237(in)X +555 5232(order)N +752(to)X +841(provide)X +1113(a)X +1176(simple,)X +1436(standard)X +1735(interface)X +2044(we)X +2165(have)X +2344(modi\256ed)X +2655(the)X +2780(database)X +3084(access)X +3317(routines)X +3602(\()X +3 f +3629(db)X +1 f +3717(\(3\)\).)X +3904(For)X +4041(the)X +4165(pur-)X +555 5322(poses)N +758(of)X +850(this)X +990(paper)X +1194(we)X +1313(call)X +1453(the)X +1575(modi\256ed)X +1883(package)X +2171(the)X +3 f +2293(Record)X +2567(Manager)X +1 f +2879(.)X +2943(Figure)X +3176(one)X +3316(shows)X +3540(the)X +3662(main)X +3846(interfaces)X +4183(and)X +555 5412(architecture)N +955(of)X +1042(LIBTP.)X + +5 p +%%Page: 5 5 +10 s 10 xH 0 xS 1 f +3 f +1 f +11 s +1851 1520(log_commit)N +2764 2077(buf_unpin)N +2764 1987(buf_get)N +3633 1408(buf_unpin)N +3633 1319(buf_pin)N +3633 1230(buf_get)N +3 f +17 s +1163 960(Txn)N +1430(M)X +1559(anager)X +2582(Record)X +3040(M)X +3169(anager)X +1 Dt +2363 726 MXY +0 355 Dl +1426 0 Dl +0 -355 Dl +-1426 0 Dl +3255 1616 MXY +0 535 Dl +534 0 Dl +0 -535 Dl +-534 0 Dl +2185 MX +0 535 Dl +535 0 Dl +0 -535 Dl +-535 0 Dl +1116 MX +0 535 Dl +534 0 Dl +0 -535 Dl +-534 0 Dl +726 MY +0 355 Dl +891 0 Dl +0 -355 Dl +-891 0 Dl +1 f +11 s +2207 1297(lock)N +2564 1386(log)N +865(unlock_all)X +1851 1609(log_unroll)N +1650 2508 MXY +0 178 Dl +1605 0 Dl +0 -178 Dl +-1605 0 Dl +1294 1616 MXY +19 -30 Dl +-19 11 Dl +-20 -11 Dl +20 30 Dl +0 -535 Dl +2319 2508 MXY +-22 -30 Dl +4 23 Dl +-18 14 Dl +36 -7 Dl +-936 -357 Dl +3277 2455(sleep_on)N +1405 1616 MXY +36 4 Dl +-18 -13 Dl +1 -22 Dl +-19 31 Dl +1070 -535 Dl +2631 2508 MXY +36 6 Dl +-18 -14 Dl +3 -22 Dl +-21 30 Dl +891 -357 Dl +1426 2455(sleep_on)N +3255 1884 MXY +-31 -20 Dl +11 20 Dl +-11 19 Dl +31 -19 Dl +-535 0 Dl +1554 2366(wake)N +3277(wake)X +2185 1884 MXY +-31 -20 Dl +12 20 Dl +-12 19 Dl +31 -19 Dl +-356 0 Dl +0 -803 Dl +3 f +17 s +1236 1851(Lock)N +1118 2030(M)N +1247(anager)X +2339 1851(Log)N +2187 2030(M)N +2316(anager)X +3333 1851(Buffer)N +3257 2030(M)N +3386(anager)X +3522 1616 MXY +20 -30 Dl +-20 11 Dl +-20 -11 Dl +20 30 Dl +0 -535 Dl +1950 2654(Process)N +2424(M)X +2553(anager)X +2542 1616 MXY +19 -30 Dl +-19 11 Dl +-20 -11 Dl +20 30 Dl +0 -535 Dl +1 f +11 s +2207 1364(unlock)N +2452 2508 MXY +20 -31 Dl +-20 11 Dl +-19 -11 Dl +19 31 Dl +0 -357 Dl +2497 2322(sleep_on)N +2497 2233(wake)N +3 Dt +-1 Ds +3 f +10 s +1790 2830(Figure)N +2037(1:)X +2144(Library)X +2435(module)X +2708(interfaces.)X +1 f +10 f +555 3010(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +3 f +555 3286(3.2.1.)N +775(The)X +928(Log)X +1081(Manager)X +1 f +755 3409(The)N +3 f +907(Log)X +1067(Manager)X +1 f +1406(enforces)X +1706(the)X +1831(write-ahead)X +2238(logging)X +2509(protocol.)X +2843(Its)X +2949(primitive)X +3268(operations)X +3628(are)X +2 f +3753(log)X +1 f +3855(,)X +2 f +3901(log_commit)X +1 f +4279(,)X +2 f +555 3499(log_read)N +1 f +844(,)X +2 f +889(log_roll)X +1 f +1171(and)X +2 f +1312(log_unroll)X +1 f +1649(.)X +1714(The)X +2 f +1864(log)X +1 f +1991(call)X +2132(performs)X +2447(a)X +2508(buffered)X +2806(write)X +2996(of)X +3088(the)X +3211(speci\256ed)X +3520(log)X +3646(record)X +3876(and)X +4016(returns)X +4263(a)X +555 3589(unique)N +809(log)X +947(sequence)X +1278(number)X +1559(\(LSN\).)X +1840(This)X +2017(LSN)X +2203(may)X +2376(then)X +2549(be)X +2660(used)X +2842(to)X +2939(retrieve)X +3220(a)X +3291(record)X +3532(from)X +3723(the)X +3856(log)X +3993(using)X +4201(the)X +2 f +555 3679(log_read)N +1 f +865(call.)X +1042(The)X +2 f +1188(log)X +1 f +1311(interface)X +1614(knows)X +1844(very)X +2008(little)X +2175(about)X +2374(the)X +2493(internal)X +2759(format)X +2993(of)X +3080(the)X +3198(log)X +3320(records)X +3577(it)X +3641(receives.)X +3965(Rather,)X +4219(all)X +555 3769(log)N +681(records)X +942(are)X +1065 0.4028(referenced)AX +1430(by)X +1534(a)X +1594(header)X +1833(structure,)X +2158(a)X +2218(log)X +2344(record)X +2574(type,)X +2756(and)X +2896(a)X +2956(character)X +3276(buffer)X +3497(containing)X +3859(the)X +3981(data)X +4138(to)X +4223(be)X +555 3859(logged.)N +834(The)X +980(log)X +1103(record)X +1330(type)X +1489(is)X +1563(used)X +1731(to)X +1814(call)X +1951(the)X +2070(appropriate)X +2457(redo)X +2621(and)X +2758(undo)X +2939(routines)X +3217(during)X +2 f +3446(abort)X +1 f +3639(and)X +2 f +3775(commit)X +1 f +4031(process-)X +555 3949(ing.)N +721(While)X +941(we)X +1059(have)X +1235(used)X +1406(the)X +3 f +1528(Log)X +1684(Manager)X +1 f +2019(to)X +2104(provide)X +2372(before)X +2601(and)X +2740(after)X +2911(image)X +3130(logging,)X +3417(it)X +3484(may)X +3645(also)X +3797(be)X +3896(used)X +4066(for)X +4183(any)X +555 4039(of)N +642(the)X +760(logging)X +1024(algorithms)X +1386(discussed.)X +755 4162(The)N +2 f +905(log_commit)X +1 f +1308(operation)X +1636(behaves)X +1920(exactly)X +2177(like)X +2322(the)X +2 f +2445(log)X +1 f +2572(operation)X +2900(but)X +3026(guarantees)X +3394(that)X +3538(the)X +3660(log)X +3786(has)X +3917(been)X +4093(forced)X +555 4252(to)N +643(disk)X +802(before)X +1034(returning.)X +1394(A)X +1478(discussion)X +1837(of)X +1930(our)X +2063(commit)X +2333(strategy)X +2613(appears)X +2884(in)X +2971(the)X +3094(implementation)X +3621(section)X +3873(\(section)X +4152(4.2\).)X +2 f +555 4342(Log_unroll)N +1 f +935(reads)X +1126(log)X +1249(records)X +1507(from)X +1684(the)X +1803(log,)X +1946(following)X +2278(backward)X +2611(transaction)X +2983(pointers)X +3261(and)X +3397(calling)X +3635(the)X +3753(appropriate)X +4139(undo)X +555 4432(routines)N +839(to)X +927(implement)X +1295(transaction)X +1673(abort.)X +1904(In)X +1997(a)X +2059(similar)X +2307(manner,)X +2 f +2594(log_roll)X +1 f +2877(reads)X +3073(log)X +3201(records)X +3464(sequentially)X +3877(forward,)X +4178(cal-)X +555 4522(ling)N +699(the)X +817(appropriate)X +1203(redo)X +1366(routines)X +1644(to)X +1726(recover)X +1988(committed)X +2350(transactions)X +2753(after)X +2921(a)X +2977(system)X +3219(crash.)X +3 f +555 4708(3.2.2.)N +775(The)X +928(Buffer)X +1171(Manager)X +1 f +755 4831(The)N +3 f +912(Buffer)X +1167(Manager)X +1 f +1511(uses)X +1681(a)X +1749(pool)X +1923(of)X +2022(shared)X +2264(memory)X +2563(to)X +2657(provide)X +2934(a)X +3002(least-recently-used)X +3641(\(LRU\))X +3886(block)X +4095(cache.)X +555 4921(Although)N +886(the)X +1013(current)X +1270(library)X +1513(provides)X +1818(an)X +1923(LRU)X +2112(cache,)X +2345(it)X +2418(would)X +2647(be)X +2752(simple)X +2994(to)X +3085(add)X +3229(alternate)X +3534(replacement)X +3955(policies)X +4232(as)X +555 5011(suggested)N +903(by)X +1015([CHOU85])X +1408(or)X +1507(to)X +1601(provide)X +1878(multiple)X +2176(buffer)X +2405(pools)X +2610(with)X +2784(different)X +3092(policies.)X +3412(Transactions)X +3853(request)X +4116(pages)X +555 5101(from)N +736(the)X +859(buffer)X +1081(manager)X +1383(and)X +1524(keep)X +1701(them)X +3 f +1886(pinned)X +1 f +2145(to)X +2232(ensure)X +2466(that)X +2610(they)X +2772(are)X +2895(not)X +3021(written)X +3272(to)X +3358(disk)X +3515(while)X +3717(they)X +3879(are)X +4002(in)X +4088(a)X +4148(logi-)X +555 5191(cally)N +732(inconsistent)X +1135(state.)X +1343(When)X +1556(page)X +1729(replacement)X +2143(is)X +2217(necessary,)X +2571(the)X +3 f +2689(Buffer)X +2932(Manager)X +1 f +3264(\256nds)X +3439(an)X +3535(unpinned)X +3853(page)X +4025(and)X +4161(then)X +555 5281(checks)N +794(with)X +956(the)X +3 f +1074(Log)X +1227(Manager)X +1 f +1559(to)X +1641(ensure)X +1871(that)X +2011(the)X +2129(write-ahead)X +2529(protocol)X +2816(is)X +2889(enforced.)X +3 f +555 5467(3.2.3.)N +775(The)X +928(Lock)X +1121(Manager)X +1 f +755 5590(The)N +3 f +901(Lock)X +1095(Manager)X +1 f +1428(supports)X +1720(general)X +1978(purpose)X +2253(locking)X +2514(\(single)X +2753(writer,)X +2986(multiple)X +3273(readers\))X +3553(which)X +3769(is)X +3842(currently)X +4152(used)X +555 5680(to)N +638(provide)X +904(two-phase)X +1254(locking)X +1514(and)X +1650(high)X +1812(concurrency)X +2230(B-tree)X +2451(locking.)X +2751(However,)X +3086(the)X +3204(general)X +3461(purpose)X +3735(nature)X +3956(of)X +4043(the)X +4161(lock)X + +6 p +%%Page: 6 6 +10 s 10 xH 0 xS 1 f +3 f +1 f +555 630(manager)N +857(provides)X +1158(the)X +1281(ability)X +1510(to)X +1597(support)X +1862(a)X +1923(variety)X +2171(of)X +2263(locking)X +2528(protocols.)X +2890(Currently,)X +3241(all)X +3345(locks)X +3538(are)X +3661(issued)X +3885(at)X +3967(the)X +4089(granu-)X +555 720(larity)N +747(of)X +837(a)X +896(page)X +1071(\(the)X +1219(size)X +1367(of)X +1457(a)X +1516(buffer)X +1736(in)X +1821(the)X +1942(buffer)X +2161(pool\))X +2352(which)X +2570(is)X +2645(identi\256ed)X +2969(by)X +3071(two)X +3213(4-byte)X +3440(integers)X +3716(\(a)X +3801(\256le)X +3925(id)X +4009(and)X +4147(page)X +555 810(number\).)N +898(This)X +1071(provides)X +1378(the)X +1507(necessary)X +1851(information)X +2259(to)X +2351(extend)X +2595(the)X +3 f +2723(Lock)X +2926(Manager)X +1 f +3268(to)X +3360(perform)X +3649(hierarchical)X +4059(locking)X +555 900([GRAY76].)N +982(The)X +1133(current)X +1387(implementation)X +1915(does)X +2088(not)X +2216(support)X +2482(locks)X +2677(at)X +2760(other)X +2950(granularities)X +3376(and)X +3517(does)X +3689(not)X +3816(promote)X +4108(locks;)X +555 990(these)N +740(are)X +859(obvious)X +1132(future)X +1344(additions)X +1657(to)X +1739(the)X +1857(system.)X +755 1113(If)N +831(an)X +929(incoming)X +1253(lock)X +1413(request)X +1667(cannot)X +1903(be)X +2001(granted,)X +2284(the)X +2404(requesting)X +2760(process)X +3023(is)X +3098(queued)X +3352(for)X +3467(the)X +3586(lock)X +3745(and)X +3882(descheduled.)X +555 1203(When)N +769(a)X +827(lock)X +987(is)X +1062(released,)X +1368(the)X +1488(wait)X +1647(queue)X +1860(is)X +1934(traversed)X +2250(and)X +2387(any)X +2524(newly)X +2741(compatible)X +3118(locks)X +3308(are)X +3428(granted.)X +3730(Locks)X +3947(are)X +4067(located)X +555 1293(via)N +680(a)X +743(\256le)X +872(and)X +1015(page)X +1194(hash)X +1368(table)X +1551(and)X +1694(are)X +1820(chained)X +2097(both)X +2266(by)X +2373(object)X +2595(and)X +2737(by)X +2843(transaction,)X +3241(facilitating)X +3614(rapid)X +3805(traversal)X +4108(of)X +4201(the)X +555 1383(lock)N +713(table)X +889(during)X +1118(transaction)X +1490(commit)X +1754(and)X +1890(abort.)X +755 1506(The)N +907(primary)X +1188(interfaces)X +1528(to)X +1617(the)X +1742(lock)X +1907(manager)X +2211(are)X +2 f +2337(lock)X +1 f +2471(,)X +2 f +2518(unlock)X +1 f +2732(,)X +2779(and)X +2 f +2922(lock_unlock_all)X +1 f +3434(.)X +2 f +3500(Lock)X +1 f +3682(obtains)X +3939(a)X +4001(new)X +4161(lock)X +555 1596(for)N +680(a)X +747(speci\256c)X +1023(object.)X +1290(There)X +1509(are)X +1638(also)X +1797(two)X +1947(variants)X +2231(of)X +2328(the)X +2 f +2456(lock)X +1 f +2620(request,)X +2 f +2902(lock_upgrade)X +1 f +3373(and)X +2 f +3519(lock_downgrade)X +1 f +4053(,)X +4103(which)X +555 1686(allow)N +755(the)X +875(caller)X +1076(to)X +1160(atomically)X +1519(trade)X +1701(a)X +1758(lock)X +1917(of)X +2005(one)X +2142(type)X +2301(for)X +2416(a)X +2473(lock)X +2632(of)X +2720(another.)X +2 f +3022(Unlock)X +1 f +3275(releases)X +3551(a)X +3608(speci\256c)X +3874(mode)X +4073(of)X +4161(lock)X +555 1776(on)N +655(a)X +711(speci\256c)X +976(object.)X +2 f +1232(Lock_unlock_all)X +1 f +1786(releases)X +2061(all)X +2161(the)X +2279(locks)X +2468(associated)X +2818(with)X +2980(a)X +3036(speci\256c)X +3301(transaction.)X +3 f +555 1962(3.2.4.)N +775(The)X +928(Process)X +1207(Manager)X +1 f +755 2085(The)N +3 f +900(Process)X +1179(Manager)X +1 f +1511(acts)X +1656(as)X +1743(a)X +1799(user-level)X +2136(scheduler)X +2464(to)X +2546(make)X +2740(processes)X +3068(wait)X +3226(on)X +3326(unavailable)X +3716(locks)X +3905(and)X +4041(pending)X +555 2175(buffer)N +778(cache)X +988(I/O.)X +1161(For)X +1297(each)X +1470(process,)X +1756(a)X +1817(semaphore)X +2190(is)X +2268(maintained)X +2649(upon)X +2834(which)X +3055(that)X +3200(process)X +3466(waits)X +3660(when)X +3859(it)X +3928(needs)X +4136(to)X +4223(be)X +555 2265(descheduled.)N +1014(When)X +1228(a)X +1286(process)X +1549(needs)X +1754(to)X +1838(be)X +1936(run,)X +2084(its)X +2180(semaphore)X +2549(is)X +2623(cleared,)X +2897(and)X +3034(the)X +3153(operating)X +3477(system)X +3720(reschedules)X +4116(it.)X +4201(No)X +555 2355(sophisticated)N +1002(scheduling)X +1378(algorithm)X +1718(is)X +1799(applied;)X +2085(if)X +2162(the)X +2288(lock)X +2454(for)X +2576(which)X +2800(a)X +2864(process)X +3133(was)X +3286(waiting)X +3554(becomes)X +3863(available,)X +4201(the)X +555 2445(process)N +824(is)X +905(made)X +1107(runnable.)X +1456(It)X +1533(would)X +1761(have)X +1941(been)X +2121(possible)X +2411(to)X +2501(change)X +2757(the)X +2883(kernel's)X +3170(process)X +3439(scheduler)X +3775(to)X +3865(interact)X +4134(more)X +555 2535(ef\256ciently)N +900(with)X +1062(the)X +1180(lock)X +1338(manager,)X +1655(but)X +1777(doing)X +1979(so)X +2070(would)X +2290(have)X +2462(compromised)X +2918(our)X +3045(commitment)X +3469(to)X +3551(a)X +3607(user-level)X +3944(package.)X +3 f +555 2721(3.2.5.)N +775(The)X +928(Transaction)X +1361(Manager)X +1 f +755 2844(The)N +3 f +901(Transaction)X +1335(Manager)X +1 f +1668(provides)X +1965(the)X +2084(standard)X +2377(interface)X +2680(of)X +2 f +2768(txn_begin)X +1 f +3084(,)X +2 f +3125(txn_commit)X +1 f +3499(,)X +3540(and)X +2 f +3676(txn_abort)X +1 f +3987(.)X +4047(It)X +4116(keeps)X +555 2934(track)N +742(of)X +835(all)X +941(active)X +1159(transactions,)X +1588(assigns)X +1845(unique)X +2089(transaction)X +2467(identi\256ers,)X +2833(and)X +2974(directs)X +3213(the)X +3336(abort)X +3526(and)X +3667(commit)X +3936(processing.)X +555 3024(When)N +772(a)X +2 f +833(txn_begin)X +1 f +1174(is)X +1252(issued,)X +1497(the)X +3 f +1620(Transaction)X +2058(Manager)X +1 f +2395(assigns)X +2651(the)X +2773(next)X +2935(available)X +3249(transaction)X +3625(identi\256er,)X +3958(allocates)X +4263(a)X +555 3114(per-process)N +948(transaction)X +1322(structure)X +1625(in)X +1709(shared)X +1941(memory,)X +2249(increments)X +2622(the)X +2741(count)X +2940(of)X +3028(active)X +3241(transactions,)X +3665(and)X +3802(returns)X +4046(the)X +4165(new)X +555 3204(transaction)N +937(identi\256er)X +1256(to)X +1348(the)X +1476(calling)X +1724(process.)X +2034(The)X +2188(in-memory)X +2573(transaction)X +2954(structure)X +3264(contains)X +3560(a)X +3625(pointer)X +3881(into)X +4034(the)X +4161(lock)X +555 3294(table)N +734(for)X +851(locks)X +1043(held)X +1204(by)X +1307(this)X +1445(transaction,)X +1840(the)X +1961(last)X +2095(log)X +2220(sequence)X +2538(number,)X +2826(a)X +2885(transaction)X +3260(state)X +3430(\()X +2 f +3457(idle)X +1 f +(,)S +2 f +3620(running)X +1 f +3873(,)X +2 f +3915(aborting)X +1 f +4190(,)X +4232(or)X +2 f +555 3384(committing\))N +1 f +942(,)X +982(an)X +1078(error)X +1255(code,)X +1447(and)X +1583(a)X +1639(semaphore)X +2007(identi\256er.)X +755 3507(At)N +859(commit,)X +1147(the)X +3 f +1269(Transaction)X +1706(Manager)X +1 f +2042(calls)X +2 f +2213(log_commit)X +1 f +2615(to)X +2700(record)X +2929(the)X +3050(end)X +3189(of)X +3279(transaction)X +3654(and)X +3793(to)X +3878(\257ush)X +4056(the)X +4177(log.)X +555 3597(Then)N +743(it)X +810(directs)X +1047(the)X +3 f +1168(Lock)X +1364(Manager)X +1 f +1699(to)X +1784(release)X +2031(all)X +2134(locks)X +2325(associated)X +2677(with)X +2841(the)X +2961(given)X +3161(transaction.)X +3575(If)X +3651(a)X +3709(transaction)X +4083(aborts,)X +555 3687(the)N +3 f +680(Transaction)X +1120(Manager)X +1 f +1459(calls)X +1633(on)X +2 f +1739(log_unroll)X +1 f +2102(to)X +2190(read)X +2355(the)X +2479(transaction's)X +2915(log)X +3043(records)X +3306(and)X +3448(undo)X +3634(any)X +3776(modi\256cations)X +4237(to)X +555 3777(the)N +673(database.)X +1010(As)X +1119(in)X +1201(the)X +1319(commit)X +1583(case,)X +1762(it)X +1826(then)X +1984(calls)X +2 f +2151(lock_unlock_all)X +1 f +2683(to)X +2765(release)X +3009(the)X +3127(transaction's)X +3557(locks.)X +3 f +555 3963(3.2.6.)N +775(The)X +928(Record)X +1198(Manager)X +1 f +755 4086(The)N +3 f +919(Record)X +1208(Manager)X +1 f +1559(supports)X +1869(the)X +2006(abstraction)X +2397(of)X +2503(reading)X +2783(and)X +2938(writing)X +3208(records)X +3484(to)X +3585(a)X +3660(database.)X +3996(We)X +4147(have)X +555 4176(modi\256ed)N +861(the)X +981(the)X +1101(database)X +1399(access)X +1626(routines)X +3 f +1905(db)X +1 f +1993(\(3\))X +2108([BSD91])X +2418(to)X +2501(call)X +2638(the)X +2757(log,)X +2900(lock,)X +3079(and)X +3216(buffer)X +3434(managers.)X +3803(In)X +3891(order)X +4082(to)X +4165(pro-)X +555 4266(vide)N +718(functionality)X +1152(to)X +1239(perform)X +1523(undo)X +1708(and)X +1849(redo,)X +2037(the)X +3 f +2160(Record)X +2434(Manager)X +1 f +2770(de\256nes)X +3021(a)X +3081(collection)X +3421(of)X +3512(log)X +3638(record)X +3868(types)X +4061(and)X +4201(the)X +555 4356(associated)N +920(undo)X +1115(and)X +1266(redo)X +1444(routines.)X +1777(The)X +3 f +1937(Log)X +2105(Manager)X +1 f +2452(performs)X +2777(a)X +2848(table)X +3039(lookup)X +3296(on)X +3411(the)X +3543(record)X +3783(type)X +3955(to)X +4051(call)X +4201(the)X +555 4446(appropriate)N +951(routines.)X +1299(For)X +1440(example,)X +1762(the)X +1890(B-tree)X +2121(access)X +2356(method)X +2625(requires)X +2913(two)X +3062(log)X +3193(record)X +3428(types:)X +3648(insert)X +3855(and)X +4000(delete.)X +4241(A)X +555 4536(replace)N +808(operation)X +1131(is)X +1204(implemented)X +1642(as)X +1729(a)X +1785(delete)X +1997(followed)X +2302(by)X +2402(an)X +2498(insert)X +2696(and)X +2832(is)X +2905(logged)X +3143(accordingly.)X +3 f +555 4722(3.3.)N +715(Application)X +1134(Architectures)X +1 f +755 4845(The)N +907(structure)X +1215(of)X +1309(LIBTP)X +1558(allows)X +1794(application)X +2177(designers)X +2507(to)X +2596(trade)X +2784(off)X +2905(performance)X +3339(and)X +3481(protection.)X +3872(Since)X +4076(a)X +4138(large)X +555 4935(portion)N +810(of)X +901(LIBTP's)X +1205(functionality)X +1638(is)X +1715(provided)X +2024(by)X +2128(managing)X +2468(structures)X +2804(in)X +2889(shared)X +3122(memory,)X +3432(its)X +3530(structures)X +3865(are)X +3987(subject)X +4237(to)X +555 5025(corruption)N +926(by)X +1043(applications)X +1467(when)X +1678(the)X +1813(library)X +2064(is)X +2154(linked)X +2391(directly)X +2673(with)X +2852(the)X +2987(application.)X +3420(For)X +3568(this)X +3720(reason,)X +3987(LIBTP)X +4246(is)X +555 5115(designed)N +864(to)X +950(allow)X +1152(compilation)X +1558(into)X +1706(a)X +1766(separate)X +2053(server)X +2273(process)X +2537(which)X +2756(may)X +2917(be)X +3016(accessed)X +3321(via)X +3442(a)X +3501(socket)X +3729(interface.)X +4094(In)X +4184(this)X +555 5205(way)N +712(LIBTP's)X +1015(data)X +1172(structures)X +1507(are)X +1629(protected)X +1951(from)X +2130(application)X +2509(code,)X +2704(but)X +2829(communication)X +3349(overhead)X +3666(is)X +3741(increased.)X +4107(When)X +555 5295(applications)N +975(are)X +1107(trusted,)X +1377(LIBTP)X +1631(may)X +1801(be)X +1909(compiled)X +2239(directly)X +2516(into)X +2672(the)X +2802(application)X +3190(providing)X +3533(improved)X +3872(performance.)X +555 5385(Figures)N +815(two)X +955(and)X +1091(three)X +1272(show)X +1461(the)X +1579(two)X +1719(alternate)X +2016(application)X +2392(architectures.)X +755 5508(There)N +964(are)X +1084(potentially)X +1447(two)X +1588(modes)X +1818(in)X +1901(which)X +2118(one)X +2255(might)X +2462(use)X +2590(LIBTP)X +2833(in)X +2916(a)X +2972(server)X +3189(based)X +3392(architecture.)X +3832(In)X +3919(the)X +4037(\256rst,)X +4201(the)X +555 5598(server)N +778(would)X +1004(provide)X +1275(the)X +1399(capability)X +1741(to)X +1829(respond)X +2109(to)X +2197(requests)X +2486(to)X +2574(each)X +2747(of)X +2839(the)X +2962(low)X +3107(level)X +3288(modules)X +3584(\(lock,)X +3794(log,)X +3941(buffer,)X +4183(and)X +555 5688(transaction)N +944(managers\).)X +1356(Unfortunately,)X +1863(the)X +1998(performance)X +2442(of)X +2546(such)X +2730(a)X +2803(system)X +3062(is)X +3152(likely)X +3371(to)X +3470(be)X +3583(blindingly)X +3947(slow)X +4134(since)X + +7 p +%%Page: 7 7 +10 s 10 xH 0 xS 1 f +3 f +1 f +1 Dt +1864 1125 MXY +15 -26 Dl +-15 10 Dl +-14 -10 Dl +14 26 Dl +0 -266 Dl +1315 1125 MXY +15 -26 Dl +-15 10 Dl +-14 -10 Dl +14 26 Dl +0 -266 Dl +3 Dt +1133 1125 MXY +0 798 Dl +931 0 Dl +0 -798 Dl +-931 0 Dl +1 Dt +1266 1257 MXY +0 133 Dl +665 0 Dl +0 -133 Dl +-665 0 Dl +3 f +8 s +1513 1351(driver)N +1502 1617(LIBTP)N +1266 1390 MXY +0 400 Dl +665 0 Dl +0 -400 Dl +-665 0 Dl +3 Dt +1133 726 MXY +0 133 Dl +931 0 Dl +0 -133 Dl +-931 0 Dl +1 f +1029 1098(txn_abort)N +964 1015(txn_commit)N +1018 932(txn_begin)N +1910 1015(db_ops)N +3 f +1308 820(Application)N +1645(Program)X +1398 1218(Server)N +1594(Process)X +1 f +1390 986(socket)N +1569(interface)X +1 Dt +1848 967 MXY +-23 -14 Dl +8 14 Dl +-8 15 Dl +23 -15 Dl +-50 0 Dl +1324 MX +23 15 Dl +-9 -15 Dl +9 -14 Dl +-23 14 Dl +50 0 Dl +3 Dt +2862 859 MXY +0 1064 Dl +932 0 Dl +0 -1064 Dl +-932 0 Dl +1 Dt +3178 1390 MXY +24 -12 Dl +-17 0 Dl +-8 -15 Dl +1 27 Dl +150 -265 Dl +3494 1390 MXY +0 -27 Dl +-8 15 Dl +-16 1 Dl +24 11 Dl +-166 -265 Dl +3 f +3232 1617(LIBTP)N +2995 1390 MXY +0 400 Dl +666 0 Dl +0 -400 Dl +-666 0 Dl +992 MY +0 133 Dl +666 0 Dl +0 -133 Dl +-666 0 Dl +3168 1086(Application)N +1 f +2939 1201(txn_begin)N +2885 1284(txn_commit)N +2950 1368(txn_abort)N +3465 1284(db_ops)N +3 f +3155 766(Single)N +3339(Process)X +3 Dt +-1 Ds +811 2100(Figure)N +1023(2:)X +1107(Server)X +1318(Architecture.)X +1 f +1727(In)X +1811(this)X +1934(con\256guration,)X +811 2190(the)N +916(library)X +1113(is)X +1183(loaded)X +1380(into)X +1507(a)X +1562(server)X +1744(process)X +1962(which)X +2145(is)X +2214(ac-)X +811 2280(cessed)N +993(via)X +1087(a)X +1131(socket)X +1310(interface.)X +3 f +2563 2100(Figure)N +2803(3:)X +2914(Single)X +3140(Process)X +3403(Architecture.)X +1 f +3839(In)X +3950(this)X +2563 2190(con\256guration,)N +2948(the)X +3053(library)X +3250(routines)X +3483(are)X +3587(loaded)X +3784(as)X +3864(part)X +3990(of)X +2563 2280(the)N +2657(application)X +2957(and)X +3065(accessed)X +3303(via)X +3397(a)X +3441(subroutine)X +3727(interface.)X +10 s +10 f +555 2403(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +1 f +555 2679(modifying)N +909(a)X +966(piece)X +1157(of)X +1245(data)X +1400(would)X +1621(require)X +1870(three)X +2051(or)X +2138(possibly)X +2424(four)X +2578(separate)X +2862(communications:)X +3433(one)X +3569(to)X +3651(lock)X +3809(the)X +3927(data,)X +4101(one)X +4237(to)X +555 2769(obtain)N +781(the)X +905(data,)X +1085(one)X +1227(to)X +1315(log)X +1443(the)X +1567(modi\256cation,)X +2017(and)X +2159(possibly)X +2451(one)X +2593(to)X +2681(transmit)X +2969(the)X +3093(modi\256ed)X +3403(data.)X +3583(Figure)X +3817(four)X +3976(shows)X +4201(the)X +555 2859(relative)N +826(performance)X +1263(for)X +1387(retrieving)X +1728(a)X +1793(single)X +2013(record)X +2248(using)X +2450(the)X +2577(record)X +2812(level)X +2997(call)X +3142(versus)X +3376(using)X +3578(the)X +3705(lower)X +3917(level)X +4102(buffer)X +555 2949(management)N +987(and)X +1125(locking)X +1387(calls.)X +1616(The)X +1763(2:1)X +1887(ratio)X +2056(observed)X +2367(in)X +2450(the)X +2569(single)X +2781(process)X +3043(case)X +3203(re\257ects)X +3456(the)X +3575(additional)X +3916(overhead)X +4232(of)X +555 3039(parsing)N +819(eight)X +1006(commands)X +1380(rather)X +1595(than)X +1760(one)X +1903(while)X +2108(the)X +2233(3:1)X +2362(ratio)X +2536(observed)X +2853(in)X +2942(the)X +3067(client/server)X +3491(architecture)X +3898(re\257ects)X +4157(both)X +555 3129(the)N +679(parsing)X +941(and)X +1083(the)X +1207(communication)X +1731(overheard.)X +2118(Although)X +2445(there)X +2631(may)X +2794(be)X +2895(applications)X +3307(which)X +3528(could)X +3731(tolerate)X +3997(such)X +4169(per-)X +555 3219(formance,)N +904(it)X +973(seems)X +1194(far)X +1309(more)X +1499(feasible)X +1774(to)X +1861(support)X +2126(a)X +2187(higher)X +2417(level)X +2597(interface,)X +2923(such)X +3094(as)X +3185(that)X +3329(provided)X +3638(by)X +3742(a)X +3802(query)X +4009(language)X +555 3309(\()N +2 f +582(e.g.)X +1 f +718(SQL)X +889([SQL86]\).)X +755 3432(Although)N +1081(LIBTP)X +1327(does)X +1498(not)X +1624(have)X +1800(an)X +1900(SQL)X +2075(parser,)X +2316(we)X +2433(have)X +2608(built)X +2777(a)X +2836(server)X +3056(application)X +3435(using)X +3631(the)X +3752(toolkit)X +3983(command)X +555 3522(language)N +882(\(TCL\))X +1124([OUST90].)X +1544(The)X +1706(server)X +1940(supports)X +2248(a)X +2321(command)X +2674(line)X +2831(interface)X +3150(similar)X +3409(to)X +3508(the)X +3643(subroutine)X +4017(interface)X +555 3612(de\256ned)N +811(in)X +3 f +893(db)X +1 f +981(\(3\).)X +1135(Since)X +1333(it)X +1397(is)X +1470(based)X +1673(on)X +1773(TCL,)X +1964(it)X +2028(provides)X +2324(control)X +2571(structures)X +2903(as)X +2990(well.)X +3 f +555 3798(4.)N +655(Implementation)X +1 f +3 f +555 3984(4.1.)N +715(Locking)X +1014(and)X +1162(Deadlock)X +1502(Detection)X +1 f +755 4107(LIBTP)N +1007(uses)X +1175(two-phase)X +1535(locking)X +1805(for)X +1929(user)X +2093(data.)X +2297(Strictly)X +2562(speaking,)X +2897(the)X +3024(two)X +3173(phases)X +3416(in)X +3507(two-phase)X +3866(locking)X +4135(are)X +4263(a)X +3 f +555 4197(grow)N +1 f +756(phase,)X +986(during)X +1221(which)X +1443(locks)X +1638(are)X +1763(acquired,)X +2086(and)X +2228(a)X +3 f +2290(shrink)X +1 f +2537(phase,)X +2766(during)X +3001(which)X +3223(locks)X +3418(are)X +3543(released.)X +3873(No)X +3997(lock)X +4161(may)X +555 4287(ever)N +720(be)X +822(acquired)X +1124(during)X +1358(the)X +1481(shrink)X +1706(phase.)X +1954(The)X +2104(grow)X +2294(phase)X +2502(lasts)X +2669(until)X +2840(the)X +2963(\256rst)X +3112(release,)X +3381(which)X +3602(marks)X +3823(the)X +3946(start)X +4109(of)X +4201(the)X +555 4377(shrink)N +780(phase.)X +1028(In)X +1120(practice,)X +1420(the)X +1543(grow)X +1733(phase)X +1941(lasts)X +2108(for)X +2227(the)X +2350(duration)X +2642(of)X +2734(a)X +2795(transaction)X +3172(in)X +3259(LIBTP)X +3506(and)X +3647(in)X +3734(commercial)X +4138(data-)X +555 4467(base)N +721(systems.)X +1037(The)X +1184(shrink)X +1406(phase)X +1611(takes)X +1798(place)X +1990(during)X +2221(transaction)X +2595(commit)X +2861(or)X +2950(abort.)X +3177(This)X +3341(means)X +3568(that)X +3710(locks)X +3901(are)X +4022(acquired)X +555 4557(on)N +655(demand)X +929(during)X +1158(the)X +1276(lifetime)X +1545(of)X +1632(a)X +1688(transaction,)X +2080(and)X +2216(held)X +2374(until)X +2540(commit)X +2804(time,)X +2986(at)X +3064(which)X +3280(point)X +3464(all)X +3564(locks)X +3753(are)X +3872(released.)X +755 4680(If)N +832(multiple)X +1121(transactions)X +1527(are)X +1649(active)X +1864(concurrently,)X +2313(deadlocks)X +2657(can)X +2792(occur)X +2994(and)X +3133(must)X +3311(be)X +3410(detected)X +3701(and)X +3840(resolved.)X +4174(The)X +555 4770(lock)N +715(table)X +893(can)X +1027(be)X +1125(thought)X +1391(of)X +1480(as)X +1569(a)X +1627(representation)X +2104(of)X +2193(a)X +2251(directed)X +2532(graph.)X +2777(The)X +2924(nodes)X +3133(in)X +3216(the)X +3335(graph)X +3539(are)X +3659(transactions.)X +4103(Edges)X +555 4860(represent)N +878(the)X +3 f +1004(waits-for)X +1 f +1340(relation)X +1613(between)X +1909(transactions;)X +2342(if)X +2419(transaction)X +2 f +2799(A)X +1 f +2876(is)X +2957(waiting)X +3225(for)X +3347(a)X +3411(lock)X +3577(held)X +3743(by)X +3851(transaction)X +2 f +4230(B)X +1 f +4279(,)X +555 4950(then)N +716(a)X +775(directed)X +1057(edge)X +1232(exists)X +1437(from)X +2 f +1616(A)X +1 f +1687(to)X +2 f +1771(B)X +1 f +1842(in)X +1926(the)X +2046(graph.)X +2291(A)X +2371(deadlock)X +2683(exists)X +2887(if)X +2958(a)X +3016(cycle)X +3208(appears)X +3476(in)X +3560(the)X +3680(graph.)X +3925(By)X +4040(conven-)X +555 5040(tion,)N +719(no)X +819(transaction)X +1191(ever)X +1350(waits)X +1539(for)X +1653(a)X +1709(lock)X +1867(it)X +1931(already)X +2188(holds,)X +2401(so)X +2492(re\257exive)X +2793(edges)X +2996(are)X +3115(impossible.)X +755 5163(A)N +836(distinguished)X +1285(process)X +1549(monitors)X +1856(the)X +1977(lock)X +2138(table,)X +2337(searching)X +2668(for)X +2785(cycles.)X +3048(The)X +3195(frequency)X +3539(with)X +3703(which)X +3921(this)X +4058(process)X +555 5253(runs)N +716(is)X +792(user-settable;)X +1243(for)X +1360(the)X +1481(multi-user)X +1833(tests)X +1998(discussed)X +2328(in)X +2413(section)X +2663(5.1.2,)X +2866(it)X +2933(has)X +3063(been)X +3238(set)X +3350(to)X +3435(wake)X +3628(up)X +3731(every)X +3932(second,)X +4197(but)X +555 5343(more)N +742(sophisticated)X +1182(schedules)X +1516(are)X +1636(certainly)X +1938(possible.)X +2261(When)X +2474(a)X +2531(cycle)X +2722(is)X +2796(detected,)X +3105(one)X +3242(of)X +3330(the)X +3449(transactions)X +3853(in)X +3936(the)X +4055(cycle)X +4246(is)X +555 5433(nominated)N +917(and)X +1057(aborted.)X +1362(When)X +1578(the)X +1700(transaction)X +2076(aborts,)X +2315(it)X +2382(rolls)X +2547(back)X +2722(its)X +2820(changes)X +3102(and)X +3241(releases)X +3519(its)X +3617(locks,)X +3829(thereby)X +4093(break-)X +555 5523(ing)N +677(the)X +795(cycle)X +985(in)X +1067(the)X +1185(graph.)X + +8 p +%%Page: 8 8 +10 s 10 xH 0 xS 1 f +3 f +1 f +4 Ds +1 Dt +1866 865 MXY +1338 0 Dl +1866 1031 MXY +1338 0 Dl +1866 1199 MXY +1338 0 Dl +1866 1366 MXY +1338 0 Dl +1866 1533 MXY +1338 0 Dl +1866 1701 MXY +1338 0 Dl +-1 Ds +5 Dt +1866 1868 MXY +1338 0 Dl +1 Dt +1 Di +2981 MX + 2981 1868 lineto + 2981 1575 lineto + 3092 1575 lineto + 3092 1868 lineto + 2981 1868 lineto +closepath 21 2981 1575 3092 1868 Dp +2646 MX + 2646 1868 lineto + 2646 949 lineto + 2758 949 lineto + 2758 1868 lineto + 2646 1868 lineto +closepath 14 2646 949 2758 1868 Dp +2312 MX + 2312 1868 lineto + 2312 1701 lineto + 2423 1701 lineto + 2423 1868 lineto + 2312 1868 lineto +closepath 3 2312 1701 2423 1868 Dp +1977 MX + 1977 1868 lineto + 1977 1512 lineto + 2089 1512 lineto + 2089 1868 lineto + 1977 1868 lineto +closepath 19 1977 1512 2089 1868 Dp +3 f +2640 2047(Client/Server)N +1957(Single)X +2185(Process)X +7 s +2957 1957(record)N +2570(component)X +2289(record)X +1890(components)X +1733 1724(.1)N +1733 1556(.2)N +1733 1389(.3)N +1733 1222(.4)N +1733 1055(.5)N +1733 889(.6)N +1590 726(Elapsed)N +1794(Time)X +1613 782(\(in)N +1693(seconds\))X +3 Dt +-1 Ds +8 s +555 2255(Figure)N +756(4:)X +829(Comparison)X +1187(of)X +1260(High)X +1416(and)X +1540(Low)X +1681(Level)X +1850(Interfaces.)X +1 f +2174(Elapsed)X +2395(time)X +2528(in)X +2597(seconds)X +2818(to)X +2887(perform)X +3111(a)X +3158(single)X +3330(record)X +3511(retrieval)X +3742(from)X +3885(a)X +3932(command)X +4203(line)X +555 2345(\(rather)N +751(than)X +888(a)X +943(procedural)X +1241(interface\))X +1510(is)X +1579(shown)X +1772(on)X +1862(the)X +1966(y)X +2024(axis.)X +2185(The)X +2310(``component'')X +2704(numbers)X +2950(re\257ect)X +3135(the)X +3239(timings)X +3458(when)X +3622(the)X +3726(record)X +3914(is)X +3983(retrieved)X +4235(by)X +555 2435(separate)N +785(calls)X +924(to)X +996(the)X +1096(lock)X +1228(manager)X +1469(and)X +1583(buffer)X +1760(manager)X +2001(while)X +2165(the)X +2264(``record'')X +2531(timings)X +2745(were)X +2889(obtained)X +3130(by)X +3215(using)X +3375(a)X +3424(single)X +3598(call)X +3711(to)X +3782(the)X +3881(record)X +4064(manager.)X +555 2525(The)N +674(2:1)X +776(ratio)X +913(observed)X +1163(for)X +1257(the)X +1355(single)X +1528(process)X +1739(case)X +1868(is)X +1930(a)X +1977(re\257ection)X +2237(of)X +2309(the)X +2406(parsing)X +2613(overhead)X +2865(for)X +2958(executing)X +3225(eight)X +3372(separate)X +3599(commands)X +3895(rather)X +4062(than)X +4191(one.)X +555 2615(The)N +673(additional)X +948(factor)X +1115(of)X +1187(one)X +1298(re\257ected)X +1536(in)X +1605(the)X +1702(3:1)X +1803(ratio)X +1939(for)X +2031(the)X +2127(client/server)X +2460(architecture)X +2794(is)X +2855(due)X +2965(to)X +3033(the)X +3129(communication)X +3545(overhead.)X +3828(The)X +3945(true)X +4062(ratios)X +4222(are)X +555 2705(actually)N +775(worse)X +945(since)X +1094(the)X +1190(component)X +1492(timings)X +1703(do)X +1785(not)X +1884(re\257ect)X +2060(the)X +2155(search)X +2334(times)X +2490(within)X +2671(each)X +2804(page)X +2941(or)X +3011(the)X +3106(time)X +3237(required)X +3466(to)X +3533(transmit)X +3760(the)X +3855(page)X +3992(between)X +4221(the)X +555 2795(two)N +667(processes.)X +10 s +10 f +555 2885(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +3 f +555 3161(4.2.)N +715(Group)X +961(Commit)X +1 f +755 3284(Since)N +959(the)X +1083(log)X +1211(must)X +1392(be)X +1494(\257ushed)X +1751(to)X +1839(disk)X +1997(at)X +2080(commit)X +2349(time,)X +2536(disk)X +2694(bandwidth)X +3057(fundamentally)X +3545(limits)X +3751(the)X +3874(rate)X +4020(at)X +4103(which)X +555 3374(transactions)N +959(complete.)X +1314(Since)X +1513(most)X +1688(transactions)X +2091(write)X +2276(only)X +2438(a)X +2494(few)X +2635(small)X +2828(records)X +3085(to)X +3167(the)X +3285(log,)X +3427(the)X +3545(last)X +3676(page)X +3848(of)X +3935(the)X +4053(log)X +4175(will)X +555 3464(be)N +658(\257ushed)X +916(once)X +1095(by)X +1202(every)X +1408(transaction)X +1787(which)X +2010(writes)X +2233(to)X +2322(it.)X +2433(In)X +2527(the)X +2652(naive)X +2853(implementation,)X +3402(these)X +3593(\257ushes)X +3841(would)X +4067(happen)X +555 3554(serially.)N +755 3677(LIBTP)N +1008(uses)X +3 f +1177(group)X +1412(commit)X +1 f +1702([DEWI84])X +2077(in)X +2170(order)X +2371(to)X +2464(amortize)X +2775(the)X +2903(cost)X +3062(of)X +3159(one)X +3305(synchronous)X +3740(disk)X +3903(write)X +4098(across)X +555 3767(multiple)N +851(transactions.)X +1304(Group)X +1539(commit)X +1812(provides)X +2117(a)X +2182(way)X +2345(for)X +2468(a)X +2533(group)X +2749(of)X +2845(transactions)X +3257(to)X +3348(commit)X +3621(simultaneously.)X +4174(The)X +555 3857(\256rst)N +709(several)X +967(transactions)X +1380(to)X +1472(commit)X +1745(write)X +1939(their)X +2115(changes)X +2403(to)X +2494(the)X +2621(in-memory)X +3006(log)X +3137(page,)X +3338(then)X +3505(sleep)X +3699(on)X +3808(a)X +3873(distinguished)X +555 3947(semaphore.)N +966(Later,)X +1179(a)X +1238(committing)X +1629(transaction)X +2004(\257ushes)X +2249(the)X +2370(page)X +2545(to)X +2630(disk,)X +2805(and)X +2943(wakes)X +3166(up)X +3268(all)X +3370(its)X +3467(sleeping)X +3756(peers.)X +3988(The)X +4135(point)X +555 4037(at)N +635(which)X +853(changes)X +1134(are)X +1255(actually)X +1531(written)X +1780(is)X +1855(determined)X +2238(by)X +2340(three)X +2523(thresholds.)X +2914(The)X +3061(\256rst)X +3207(is)X +3281(the)X +2 f +3400(group)X +3612(threshold)X +1 f +3935(and)X +4072(de\256nes)X +555 4127(the)N +674(minimum)X +1005(number)X +1271(of)X +1359(transactions)X +1763(which)X +1979(must)X +2154(be)X +2250(active)X +2462(in)X +2544(the)X +2662(system)X +2904(before)X +3130(transactions)X +3533(are)X +3652(forced)X +3878(to)X +3960(participate)X +555 4217(in)N +646(a)X +711(group)X +927(commit.)X +1240(The)X +1394(second)X +1646(is)X +1728(the)X +2 f +1855(wait)X +2021(threshold)X +1 f +2352(which)X +2577(is)X +2658(expressed)X +3003(as)X +3098(the)X +3224(percentage)X +3601(of)X +3696(active)X +3916(transactions)X +555 4307(waiting)N +826(to)X +919(be)X +1026(committed.)X +1439(The)X +1595(last)X +1737(is)X +1821(the)X +2 f +1950(logdelay)X +2257(threshold)X +1 f +2590(which)X +2816(indicates)X +3131(how)X +3299(much)X +3507(un\257ushed)X +3848(log)X +3980(should)X +4223(be)X +555 4397(allowed)N +829(to)X +911(accumulate)X +1297(before)X +1523(a)X +1579(waiting)X +1839(transaction's)X +2289(commit)X +2553(record)X +2779(is)X +2852(\257ushed.)X +755 4520(Group)N +981(commit)X +1246(can)X +1379(substantially)X +1803(improve)X +2090(performance)X +2517(for)X +2631(high-concurrency)X +3218(environments.)X +3714(If)X +3788(only)X +3950(a)X +4006(few)X +4147(tran-)X +555 4610(sactions)N +836(are)X +957(running,)X +1248(it)X +1314(is)X +1389(unlikely)X +1673(to)X +1757(improve)X +2046(things)X +2263(at)X +2343(all.)X +2485(The)X +2632(crossover)X +2962(point)X +3148(is)X +3223(the)X +3343(point)X +3529(at)X +3609(which)X +3827(the)X +3947(transaction)X +555 4700(commit)N +823(rate)X +968(is)X +1045(limited)X +1295(by)X +1399(the)X +1521(bandwidth)X +1883(of)X +1974(the)X +2096(device)X +2330(on)X +2434(which)X +2654(the)X +2776(log)X +2902(resides.)X +3189(If)X +3267(processes)X +3599(are)X +3722(trying)X +3937(to)X +4023(\257ush)X +4201(the)X +555 4790(log)N +677(faster)X +876(than)X +1034(the)X +1152(log)X +1274(disk)X +1427(can)X +1559(accept)X +1785(data,)X +1959(then)X +2117(group)X +2324(commit)X +2588(will)X +2732(increase)X +3016(the)X +3134(commit)X +3398(rate.)X +3 f +555 4976(4.3.)N +715(Kernel)X +971(Intervention)X +1418(for)X +1541(Synchronization)X +1 f +755 5099(Since)N +954(LIBTP)X +1197(uses)X +1356(data)X +1511(in)X +1594(shared)X +1825(memory)X +2113(\()X +2 f +2140(e.g.)X +1 f +2277(the)X +2395(lock)X +2553(table)X +2729(and)X +2865(buffer)X +3082(pool\))X +3271(it)X +3335(must)X +3510(be)X +3606(possible)X +3888(for)X +4002(a)X +4058(process)X +555 5189(to)N +640(acquire)X +900(exclusive)X +1226(access)X +1454(to)X +1538(shared)X +1770(data)X +1926(in)X +2010(order)X +2202(to)X +2286(prevent)X +2549(corruption.)X +2945(In)X +3034(addition,)X +3338(the)X +3458(process)X +3721(manager)X +4020(must)X +4197(put)X +555 5279(processes)N +886(to)X +971(sleep)X +1159(when)X +1356(the)X +1477(lock)X +1638(or)X +1728(buffer)X +1948(they)X +2109(request)X +2364(is)X +2440(in)X +2525(use)X +2655(by)X +2758(some)X +2950(other)X +3138(process.)X +3441(In)X +3530(the)X +3650(LIBTP)X +3894(implementa-)X +555 5385(tion)N +705(under)X +914(Ultrix)X +1131(4.0)X +7 s +5353(2)Y +10 s +5385(,)Y +1305(we)X +1424(use)X +1556(System)X +1816(V)X +1899(semaphores)X +2303(to)X +2390(provide)X +2660(this)X +2800(synchronization.)X +3377(Semaphores)X +3794(implemented)X +4237(in)X +555 5475(this)N +701(fashion)X +968(turn)X +1128(out)X +1261(to)X +1354(be)X +1461(an)X +1568(expensive)X +1920(choice)X +2161(for)X +2285(synchronization,)X +2847(because)X +3132(each)X +3310(access)X +3546(traps)X +3732(to)X +3824(the)X +3952(kernel)X +4183(and)X +8 s +10 f +555 5547(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)N +5 s +1 f +727 5625(2)N +8 s +763 5650(Ultrix)N +932(and)X +1040(DEC)X +1184(are)X +1277(trademarks)X +1576(of)X +1645(Digital)X +1839(Equipment)X +2136(Corporation.)X + +9 p +%%Page: 9 9 +8 s 8 xH 0 xS 1 f +10 s +3 f +1 f +555 630(executes)N +852(atomically)X +1210(there.)X +755 753(On)N +878(architectures)X +1314(that)X +1459(support)X +1724(atomic)X +1967(test-and-set,)X +2382(a)X +2443(much)X +2646(better)X +2854(choice)X +3089(would)X +3314(be)X +3415(to)X +3502(attempt)X +3767(to)X +3854(obtain)X +4079(a)X +4139(spin-)X +555 843(lock)N +714(with)X +877(a)X +934(test-and-set,)X +1345(and)X +1482(issue)X +1663(a)X +1720(system)X +1963(call)X +2100(only)X +2263(if)X +2333(the)X +2452(spinlock)X +2744(is)X +2818(unavailable.)X +3249(Since)X +3447(virtually)X +3738(all)X +3838(semaphores)X +4237(in)X +555 933(LIBTP)N +801(are)X +924(uncontested)X +1330(and)X +1469(are)X +1591(held)X +1752(for)X +1869(very)X +2035(short)X +2218(periods)X +2477(of)X +2567(time,)X +2752(this)X +2890(would)X +3113(improve)X +3403(performance.)X +3873(For)X +4007(example,)X +555 1023(processes)N +885(must)X +1062(acquire)X +1321(exclusive)X +1646(access)X +1874(to)X +1958(buffer)X +2177(pool)X +2341(metadata)X +2653(in)X +2737(order)X +2929(to)X +3013(\256nd)X +3159(and)X +3297(pin)X +3421(a)X +3479(buffer)X +3698(in)X +3781(shared)X +4012(memory.)X +555 1113(This)N +721(semaphore)X +1093(is)X +1170(requested)X +1502(most)X +1681(frequently)X +2034(in)X +2119(LIBTP.)X +2404(However,)X +2742(once)X +2917(it)X +2984(is)X +3060(acquired,)X +3380(only)X +3545(a)X +3604(few)X +3748(instructions)X +4144(must)X +555 1203(be)N +656(executed)X +966(before)X +1196(it)X +1264(is)X +1341(released.)X +1669(On)X +1791(one)X +1931(architecture)X +2335(for)X +2453(which)X +2673(we)X +2791(were)X +2972(able)X +3130(to)X +3216(gather)X +3441(detailed)X +3719(pro\256ling)X +4018(informa-)X +555 1293(tion,)N +729(the)X +857(cost)X +1015(of)X +1111(the)X +1238(semaphore)X +1615(calls)X +1791(accounted)X +2146(for)X +2269(25%)X +2445(of)X +2541(the)X +2668(total)X +2839(time)X +3010(spent)X +3208(updating)X +3517(the)X +3644(metadata.)X +4003(This)X +4174(was)X +555 1383(fairly)N +749(consistent)X +1089(across)X +1310(most)X +1485(of)X +1572(the)X +1690(critical)X +1933(sections.)X +755 1506(In)N +848(an)X +950(attempt)X +1216(to)X +1304(quantify)X +1597(the)X +1720(overhead)X +2040(of)X +2132(kernel)X +2358(synchronization,)X +2915(we)X +3034(ran)X +3162(tests)X +3329(on)X +3434(a)X +3495(version)X +3756(of)X +3848(4.3BSD-Reno)X +555 1596(which)N +786(had)X +937(been)X +1123(modi\256ed)X +1441(to)X +1537(support)X +1811(binary)X +2050(semaphore)X +2432(facilities)X +2742(similar)X +2998(to)X +3094(those)X +3297(described)X +3639(in)X +3735([POSIX91].)X +4174(The)X +555 1686(hardware)N +880(platform)X +1181(consisted)X +1504(of)X +1595(an)X +1695(HP300)X +1941(\(33MHz)X +2237(MC68030\))X +2612(workstation)X +3014(with)X +3180(16MBytes)X +3537(of)X +3628(main)X +3812(memory,)X +4123(and)X +4263(a)X +555 1776(600MByte)N +920(HP7959)X +1205(SCSI)X +1396(disk)X +1552(\(17)X +1682(ms)X +1798(average)X +2072(seek)X +2237(time\).)X +2468(We)X +2602(ran)X +2727(three)X +2910(sets)X +3052(of)X +3141(comparisons)X +3568(which)X +3786(are)X +3907(summarized)X +555 1866(in)N +645(\256gure)X +860(\256ve.)X +1028(In)X +1123(each)X +1299(comparison)X +1701(we)X +1823(ran)X +1954(two)X +2102(tests,)X +2292(one)X +2436(using)X +2637(hardware)X +2965(spinlocks)X +3295(and)X +3438(the)X +3563(other)X +3755(using)X +3955(kernel)X +4183(call)X +555 1956(synchronization.)N +1135(Since)X +1341(the)X +1467(test)X +1606(was)X +1758(run)X +1892(single-user,)X +2291(none)X +2474(of)X +2568(the)X +2693(the)X +2818(locks)X +3014(were)X +3198(contested.)X +3568(In)X +3662(the)X +3787(\256rst)X +3938(two)X +4085(sets)X +4232(of)X +555 2046(tests,)N +743(we)X +863(ran)X +992(the)X +1116(full)X +1253(transaction)X +1631(processing)X +2000(benchmark)X +2383(described)X +2717(in)X +2805(section)X +3058(5.1.)X +3223(In)X +3315(one)X +3456(case)X +3620(we)X +3739(ran)X +3867(with)X +4034(both)X +4201(the)X +555 2136(database)N +854(and)X +992(log)X +1116(on)X +1218(the)X +1338(same)X +1525(disk)X +1680(\(1)X +1769(Disk\))X +1969(and)X +2107(in)X +2191(the)X +2311(second,)X +2576(we)X +2692(ran)X +2817(with)X +2981(the)X +3101(database)X +3400(and)X +3538(log)X +3661(on)X +3762(separate)X +4047(disks)X +4232(\(2)X +555 2226(Disk\).)N +800(In)X +894(the)X +1019(last)X +1157(test,)X +1315(we)X +1436(wanted)X +1695(to)X +1784(create)X +2004(a)X +2067(CPU)X +2249(bound)X +2476(environment,)X +2928(so)X +3026(we)X +3146(used)X +3319(a)X +3381(database)X +3684(small)X +3883(enough)X +4145(to)X +4233(\256t)X +555 2316(completely)N +941(in)X +1033(the)X +1161(cache)X +1375(and)X +1521(issued)X +1751(read-only)X +2089(transactions.)X +2541(The)X +2695(results)X +2933(in)X +3024(\256gure)X +3240(\256ve)X +3389(express)X +3659(the)X +3786(kernel)X +4016(call)X +4161(syn-)X +555 2406(chronization)N +980(performance)X +1411(as)X +1502(a)X +1562(percentage)X +1935(of)X +2026(the)X +2148(spinlock)X +2443(performance.)X +2914(For)X +3049(example,)X +3365(in)X +3451(the)X +3573(1)X +3637(disk)X +3794(case,)X +3977(the)X +4098(kernel)X +555 2496(call)N +697(implementation)X +1225(achieved)X +1537(4.4)X +1662(TPS)X +1824(\(transactions)X +2259(per)X +2387(second\))X +2662(while)X +2865(the)X +2988(semaphore)X +3361(implementation)X +3888(achieved)X +4199(4.6)X +555 2586(TPS,)N +735(and)X +874(the)X +995(relative)X +1259(performance)X +1689(of)X +1779(the)X +1900(kernel)X +2123(synchronization)X +2657(is)X +2732(96%)X +2901(that)X +3043(of)X +3132(the)X +3252(spinlock)X +3545(\(100)X +3714(*)X +3776(4.4)X +3898(/)X +3942(4.6\).)X +4111(There)X +555 2676(are)N +674(two)X +814(striking)X +1078(observations)X +1503(from)X +1679(these)X +1864(results:)X +10 f +635 2799(g)N +1 f +755(even)X +927(when)X +1121(the)X +1239(system)X +1481(is)X +1554(disk)X +1707(bound,)X +1947(the)X +2065(CPU)X +2240(cost)X +2389(of)X +2476(synchronization)X +3008(is)X +3081(noticeable,)X +3451(and)X +10 f +635 2922(g)N +1 f +755(when)X +949(we)X +1063(are)X +1182(CPU)X +1357(bound,)X +1597(the)X +1715(difference)X +2062(is)X +2135(dramatic)X +2436(\(67%\).)X +3 f +555 3108(4.4.)N +715(Transaction)X +1148(Protected)X +1499(Access)X +1747(Methods)X +1 f +755 3231(The)N +903(B-tree)X +1127(and)X +1266(\256xed)X +1449(length)X +1671(recno)X +1872(\(record)X +2127(number\))X +2421(access)X +2649(methods)X +2942(have)X +3116(been)X +3290(modi\256ed)X +3596(to)X +3680(provide)X +3947(transaction)X +555 3321(protection.)N +941(Whereas)X +1244(the)X +1363(previously)X +1722(published)X +2054(interface)X +2357(to)X +2440(the)X +2559(access)X +2786(routines)X +3065(had)X +3202(separate)X +3487(open)X +3664(calls)X +3832(for)X +3946(each)X +4114(of)X +4201(the)X +10 f +555 3507(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +1 Dt +2978 5036 MXY + 2978 5036 lineto + 2978 4662 lineto + 3093 4662 lineto + 3093 5036 lineto + 2978 5036 lineto +closepath 21 2978 4662 3093 5036 Dp +2518 MX + 2518 5036 lineto + 2518 3960 lineto + 2633 3960 lineto + 2633 5036 lineto + 2518 5036 lineto +closepath 3 2518 3960 2633 5036 Dp +2059 MX + 2059 5036 lineto + 2059 3946 lineto + 2174 3946 lineto + 2174 5036 lineto + 2059 5036 lineto +closepath 1 2059 3946 2174 5036 Dp +3 f +7 s +2912 5141(Read-only)N +1426 3767(of)N +1487(Spinlock)X +1710(Throughput)X +1480 3710(Throughput)N +1786(as)X +1850(a)X +1892(%)X +11 s +1670 4843(20)N +1670 4614(40)N +1670 4384(60)N +1670 4155(80)N +1648 3925(100)N +7 s +2041 5141(1)N +2083(Disk)X +2490(2)X +2532(Disks)X +5 Dt +1829 5036 MXY +1494 0 Dl +4 Ds +1 Dt +1829 4806 MXY +1494 0 Dl +1829 4577 MXY +1494 0 Dl +1829 4347 MXY +1494 0 Dl +1829 4118 MXY +1494 0 Dl +1829 3888 MXY +1494 0 Dl +3 Dt +-1 Ds +8 s +555 5360(Figure)N +753(5:)X +823(Kernel)X +1028(Overhead)X +1315(for)X +1413(System)X +1625(Call)X +1756(Synchronization.)X +1 f +2254(The)X +2370(performance)X +2708(of)X +2778(the)X +2873(kernel)X +3049(call)X +3158(synchronization)X +3583(is)X +3643(expressed)X +3911(as)X +3980(a)X +4024(percentage)X +555 5450(of)N +625(the)X +720(spinlock)X +954(synchronization)X +1379(performance.)X +1749(In)X +1819(disk)X +1943(bound)X +2120(cases)X +2271(\(1)X +2341(Disk)X +2479(and)X +2588(2)X +2637(Disks\),)X +2837(we)X +2928(see)X +3026(that)X +3139(4-6%)X +3294(of)X +3364(the)X +3459(performance)X +3797(is)X +3857(lost)X +3966(due)X +4074(to)X +4140(kernel)X +555 5540(calls)N +688(while)X +846(in)X +912(the)X +1006(CPU)X +1147(bound)X +1323(case,)X +1464(we)X +1554(have)X +1690(lost)X +1799(67%)X +1932(of)X +2001(the)X +2095(performance)X +2432(due)X +2540(to)X +2606(kernel)X +2781(calls.)X + +10 p +%%Page: 10 10 +8 s 8 xH 0 xS 1 f +10 s +3 f +1 f +555 630(access)N +781(methods,)X +1092(we)X +1206(now)X +1364(have)X +1536(an)X +1632(integrated)X +1973(open)X +2149(call)X +2285(with)X +2447(the)X +2565(following)X +2896(calling)X +3134(conventions:)X +7 f +715 753(DB)N +859(*dbopen)X +1243(\(const)X +1579(char)X +1819(*file,)X +2155(int)X +2347(flags,)X +2683(int)X +2875(mode,)X +3163(DBTYPE)X +3499(type,)X +1291 843(int)N +1483(dbflags,)X +1915(const)X +2203(void)X +2443(*openinfo\))X +1 f +555 966(where)N +2 f +774(\256le)X +1 f +894(is)X +969(the)X +1089(name)X +1285(of)X +1374(the)X +1494(\256le)X +1618(being)X +1818(opened,)X +2 f +2092(\257ags)X +1 f +2265(and)X +2 f +2402(mode)X +1 f +2597(are)X +2717(the)X +2836(standard)X +3129(arguments)X +3484(to)X +3 f +3567(open)X +1 f +3731(\(2\),)X +2 f +3866(type)X +1 f +4021(is)X +4095(one)X +4232(of)X +555 1056(the)N +680(access)X +913(method)X +1180(types,)X +2 f +1396(db\257ags)X +1 f +1654(indicates)X +1966(the)X +2091(mode)X +2296(of)X +2390(the)X +2515(buffer)X +2739(pool)X +2907(and)X +3049(transaction)X +3427(protection,)X +3798(and)X +2 f +3940(openinfo)X +1 f +4246(is)X +555 1146(the)N +681(access)X +915(method)X +1183(speci\256c)X +1456(information.)X +1902(Currently,)X +2257(the)X +2383(possible)X +2673(values)X +2906(for)X +2 f +3028(db\257ags)X +1 f +3287(are)X +3414(DB_SHARED)X +3912(and)X +4055(DB_TP)X +555 1236(indicating)N +895(that)X +1035(buffers)X +1283(should)X +1516(be)X +1612(kept)X +1770(in)X +1852(a)X +1908(shared)X +2138(buffer)X +2355(pool)X +2517(and)X +2653(that)X +2793(the)X +2911(\256le)X +3033(should)X +3266(be)X +3362(transaction)X +3734(protected.)X +755 1359(The)N +900(modi\256cations)X +1355(required)X +1643(to)X +1725(add)X +1861(transaction)X +2233(protection)X +2578(to)X +2660(an)X +2756(access)X +2982(method)X +3242(are)X +3361(quite)X +3541(simple)X +3774(and)X +3910(localized.)X +715 1482(1.)N +795(Replace)X +1074(\256le)X +2 f +1196(open)X +1 f +1372(with)X +2 f +1534(buf_open)X +1 f +1832(.)X +715 1572(2.)N +795(Replace)X +1074(\256le)X +2 f +1196(read)X +1 f +1363(and)X +2 f +1499(write)X +1 f +1683(calls)X +1850(with)X +2012(buffer)X +2229(manager)X +2526(calls)X +2693(\()X +2 f +2720(buf_get)X +1 f +(,)S +2 f +3000(buf_unpin)X +1 f +3324(\).)X +715 1662(3.)N +795(Precede)X +1070(buffer)X +1287(manager)X +1584(calls)X +1751(with)X +1913(an)X +2009(appropriate)X +2395(\(read)X +2581(or)X +2668(write\))X +2880(lock)X +3038(call.)X +715 1752(4.)N +795(Before)X +1034(updates,)X +1319(issue)X +1499(a)X +1555(logging)X +1819(operation.)X +715 1842(5.)N +795(After)X +985(data)X +1139(have)X +1311(been)X +1483(accessed,)X +1805(release)X +2049(the)X +2167(buffer)X +2384(manager)X +2681(pin.)X +715 1932(6.)N +795(Provide)X +1064(undo/redo)X +1409(code)X +1581(for)X +1695(each)X +1863(type)X +2021(of)X +2108(log)X +2230(record)X +2456(de\256ned.)X +555 2071(The)N +702(following)X +1035(code)X +1209(fragments)X +1552(show)X +1743(how)X +1903(to)X +1987(transaction)X +2361(protect)X +2606(several)X +2856(updates)X +3123(to)X +3206(a)X +3263(B-tree.)X +7 s +3484 2039(3)N +10 s +3533 2071(In)N +3621(the)X +3740(unprotected)X +4140(case,)X +555 2161(an)N +652(open)X +829(call)X +966(is)X +1040(followed)X +1346(by)X +1447(a)X +1504(read)X +1664(call)X +1801(to)X +1884(obtain)X +2105(the)X +2224(meta-data)X +2562(for)X +2677(the)X +2796(B-tree.)X +3058(Instead,)X +3331(we)X +3446(issue)X +3627(an)X +3724(open)X +3901(to)X +3984(the)X +4102(buffer)X +555 2251(manager)N +852(to)X +934(obtain)X +1154(a)X +1210(\256le)X +1332(id)X +1414(and)X +1550(a)X +1606(buffer)X +1823(request)X +2075(to)X +2157(obtain)X +2377(the)X +2495(meta-data)X +2832(as)X +2919(shown)X +3148(below.)X +7 f +715 2374(char)N +955(*path;)X +715 2464(int)N +907(fid,)X +1147(flags,)X +1483(len,)X +1723(mode;)X +715 2644(/*)N +859(Obtain)X +1195(a)X +1291(file)X +1531(id)X +1675(with)X +1915(which)X +2203(to)X +2347(access)X +2683(the)X +2875(buffer)X +3211(pool)X +3451(*/)X +715 2734(fid)N +907(=)X +1003(buf_open\(path,)X +1723(flags,)X +2059(mode\);)X +715 2914(/*)N +859(Read)X +1099(the)X +1291(meta)X +1531(data)X +1771(\(page)X +2059(0\))X +2203(for)X +2395(the)X +2587(B-tree)X +2923(*/)X +715 3004(if)N +859(\(tp_lock\(fid,)X +1531(0,)X +1675(READ_LOCK\)\))X +1003 3094(return)N +1339(error;)X +715 3184(meta_data_ptr)N +1387(=)X +1483(buf_get\(fid,)X +2107(0,)X +2251(BF_PIN,)X +2635(&len\);)X +1 f +555 3307(The)N +714(BF_PIN)X +1014(argument)X +1350(to)X +2 f +1445(buf_get)X +1 f +1718(indicates)X +2036(that)X +2189(we)X +2316(wish)X +2500(to)X +2595(leave)X +2798(this)X +2946(page)X +3131(pinned)X +3382(in)X +3477(memory)X +3777(so)X +3881(that)X +4034(it)X +4111(is)X +4197(not)X +555 3397(swapped)N +862(out)X +990(while)X +1194(we)X +1314(are)X +1439(accessing)X +1772(it.)X +1881(The)X +2031(last)X +2167(argument)X +2495(to)X +2 f +2582(buf_get)X +1 f +2847(returns)X +3095(the)X +3218(number)X +3488(of)X +3580(bytes)X +3774(on)X +3879(the)X +4002(page)X +4179(that)X +555 3487(were)N +732(valid)X +912(so)X +1003(that)X +1143(the)X +1261(access)X +1487(method)X +1747(may)X +1905(initialize)X +2205(the)X +2323(page)X +2495(if)X +2564(necessary.)X +755 3610(Next,)N +955(consider)X +1251(inserting)X +1555(a)X +1615(record)X +1845(on)X +1949(a)X +2009(particular)X +2341(page)X +2517(of)X +2608(a)X +2668(B-tree.)X +2932(In)X +3022(the)X +3143(unprotected)X +3545(case,)X +3727(we)X +3844(read)X +4006(the)X +4127(page,)X +555 3700(call)N +2 f +693(_bt_insertat)X +1 f +1079(,)X +1121(and)X +1258(write)X +1444(the)X +1563(page.)X +1776(Instead,)X +2049(we)X +2164(lock)X +2323(the)X +2442(page,)X +2635(request)X +2888(the)X +3007(buffer,)X +3245(log)X +3368(the)X +3487(change,)X +3756(modify)X +4008(the)X +4127(page,)X +555 3790(and)N +691(release)X +935(the)X +1053(buffer.)X +7 f +715 3913(int)N +907(fid,)X +1147(len,)X +1387(pageno;)X +1867(/*)X +2011(Identifies)X +2539(the)X +2731(buffer)X +3067(*/)X +715 4003(int)N +907(index;)X +1867(/*)X +2011(Location)X +2443(at)X +2587(which)X +2875(to)X +3019(insert)X +3355(the)X +3547(new)X +3739(pair)X +3979(*/)X +715 4093(DBT)N +907(*keyp,)X +1243(*datap;)X +1867(/*)X +2011(Key/Data)X +2443(pair)X +2683(to)X +2827(be)X +2971(inserted)X +3403(*/)X +715 4183(DATUM)N +1003(*d;)X +1867(/*)X +2011(Key/data)X +2443(structure)X +2923(to)X +3067(insert)X +3403(*/)X +715 4363(/*)N +859(Lock)X +1099(and)X +1291(request)X +1675(the)X +1867(buffer)X +2203(*/)X +715 4453(if)N +859(\(tp_lock\(fid,)X +1531(pageno,)X +1915(WRITE_LOCK\)\))X +1003 4543(return)N +1339(error;)X +715 4633(buffer_ptr)N +1243(=)X +1339(buf_get\(fid,)X +1963(pageno,)X +2347(BF_PIN,)X +2731(&len\);)X +715 4813(/*)N +859(Log)X +1051(and)X +1243(perform)X +1627(the)X +1819(update)X +2155(*/)X +715 4903(log_insdel\(BTREE_INSERT,)N +1915(fid,)X +2155(pageno,)X +2539(keyp,)X +2827(datap\);)X +715 4993(_bt_insertat\(buffer_ptr,)N +1915(d,)X +2059(index\);)X +715 5083(buf_unpin\(buffer_ptr\);)N +1 f +555 5206(Succinctly,)N +942(the)X +1068(algorithm)X +1407(for)X +1529(turning)X +1788(unprotected)X +2195(code)X +2375(into)X +2527(protected)X +2854(code)X +3034(is)X +3115(to)X +3205(replace)X +3466(read)X +3633(operations)X +3995(with)X +2 f +4165(lock)X +1 f +555 5296(and)N +2 f +691(buf_get)X +1 f +951(operations)X +1305(and)X +1441(write)X +1626(operations)X +1980(with)X +2 f +2142(log)X +1 f +2264(and)X +2 f +2400(buf_unpin)X +1 f +2744(operations.)X +8 s +10 f +555 5458(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)N +5 s +1 f +727 5536(3)N +8 s +766 5561(The)N +884(following)X +1152(code)X +1291(fragments)X +1565(are)X +1661(examples,)X +1937(but)X +2038(do)X +2120(not)X +2220(de\256ne)X +2394(the)X +2490(\256nal)X +2622(interface.)X +2894(The)X +3011(\256nal)X +3143(interface)X +3383(will)X +3501(be)X +3579(determined)X +3884(after)X +4018(LIBTP)X +4214(has)X +555 5633(been)N +691(fully)X +828(integrated)X +1099(with)X +1229(the)X +1323(most)X +1464(recent)X +3 f +1635(db)X +1 f +1707(\(3\))X +1797(release)X +1989(from)X +2129(the)X +2223(Computer)X +2495(Systems)X +2725(Research)X +2974(Group)X +3153(at)X +3215(University)X +3501(of)X +3570(California,)X +3861(Berkeley.)X + +11 p +%%Page: 11 11 +8 s 8 xH 0 xS 1 f +10 s +3 f +555 630(5.)N +655(Performance)X +1 f +755 753(In)N +845(this)X +983(section,)X +1253(we)X +1370(present)X +1625(the)X +1746(results)X +1978(of)X +2067(two)X +2209(very)X +2374(different)X +2673(benchmarks.)X +3103(The)X +3250(\256rst)X +3396(is)X +3471(an)X +3569(online)X +3791(transaction)X +4165(pro-)X +555 843(cessing)N +824(benchmark,)X +1234(similar)X +1489(to)X +1584(the)X +1715(standard)X +2020(TPCB,)X +2272(but)X +2407(has)X +2547(been)X +2732(adapted)X +3015(to)X +3110(run)X +3250(in)X +3345(a)X +3414(desktop)X +3696(environment.)X +4174(The)X +555 933(second)N +798(emulates)X +1103(a)X +1159(computer-aided)X +1683(design)X +1912(environment)X +2337(and)X +2473(provides)X +2769(more)X +2954(complex)X +3250(query)X +3453(processing.)X +3 f +555 1119(5.1.)N +715(Transaction)X +1148(Processing)X +1533(Benchmark)X +1 f +755 1242(For)N +887(this)X +1023(section,)X +1291(all)X +1392(performance)X +1820(numbers)X +2117(shown)X +2346(except)X +2576(for)X +2690(the)X +2808(commercial)X +3207(database)X +3504(system)X +3746(were)X +3923(obtained)X +4219(on)X +555 1332(a)N +614(DECstation)X +1009(5000/200)X +1333(with)X +1497(32MBytes)X +1852(of)X +1941(memory)X +2230(running)X +2501(Ultrix)X +2714(V4.0,)X +2914(accessing)X +3244(a)X +3302(DEC)X +3484(RZ57)X +3688(1GByte)X +3959(disk)X +4114(drive.)X +555 1422(The)N +720(commercial)X +1139(relational)X +1482(database)X +1799(system)X +2061(tests)X +2242(were)X +2438(run)X +2584(on)X +2703(a)X +2778(comparable)X +3192(machine,)X +3523(a)X +3598(Sparcstation)X +4033(1+)X +4157(with)X +555 1512(32MBytes)N +915(memory)X +1209(and)X +1352(a)X +1415(1GByte)X +1691(external)X +1976(disk)X +2135(drive.)X +2366(The)X +2517(database,)X +2840(binaries)X +3120(and)X +3262(log)X +3390(resided)X +3648(on)X +3754(the)X +3878(same)X +4069(device.)X +555 1602(Reported)N +869(times)X +1062(are)X +1181(the)X +1299(means)X +1524(of)X +1611(\256ve)X +1751(tests)X +1913(and)X +2049(have)X +2221(standard)X +2513(deviations)X +2862(within)X +3086(two)X +3226(percent)X +3483(of)X +3570(the)X +3688(mean.)X +755 1725(The)N +905(test)X +1041(database)X +1343(was)X +1493(con\256gured)X +1861(according)X +2203(to)X +2290(the)X +2413(TPCB)X +2637(scaling)X +2889(rules)X +3070(for)X +3189(a)X +3250(10)X +3355(transaction)X +3732(per)X +3860(second)X +4108(\(TPS\))X +555 1815(system)N +817(with)X +999(1,000,000)X +1359(account)X +1649(records,)X +1946(100)X +2106(teller)X +2311(records,)X +2607(and)X +2762(10)X +2881(branch)X +3139(records.)X +3455(Where)X +3709(TPS)X +3885(numbers)X +4200(are)X +555 1905(reported,)N +865(we)X +981(are)X +1102(running)X +1373(a)X +1431(modi\256ed)X +1737(version)X +1995(of)X +2084(the)X +2203(industry)X +2486(standard)X +2779(transaction)X +3152(processing)X +3516(benchmark,)X +3914(TPCB.)X +4174(The)X +555 1995(TPCB)N +780(benchmark)X +1163(simulates)X +1491(a)X +1553(withdrawal)X +1940(performed)X +2301(by)X +2407(a)X +2469(hypothetical)X +2891(teller)X +3082(at)X +3166(a)X +3228(hypothetical)X +3650(bank.)X +3872(The)X +4022(database)X +555 2085(consists)N +831(of)X +921(relations)X +1220(\(\256les\))X +1430(for)X +1547(accounts,)X +1871(branches,)X +2200(tellers,)X +2439(and)X +2578(history.)X +2863(For)X +2997(each)X +3168(transaction,)X +3563(the)X +3684(account,)X +3976(teller,)X +4183(and)X +555 2175(branch)N +795(balances)X +1093(must)X +1269(be)X +1366(updated)X +1641(to)X +1724(re\257ect)X +1946(the)X +2065(withdrawal)X +2447(and)X +2584(a)X +2640(history)X +2882(record)X +3108(is)X +3181(written)X +3428(which)X +3644(contains)X +3931(the)X +4049(account)X +555 2265(id,)N +657(branch)X +896(id,)X +998(teller)X +1183(id,)X +1285(and)X +1421(the)X +1539(amount)X +1799(of)X +1886(the)X +2004(withdrawal)X +2385([TPCB90].)X +755 2388(Our)N +914(implementation)X +1450(of)X +1551(the)X +1683(benchmark)X +2074(differs)X +2317(from)X +2506(the)X +2637(speci\256cation)X +3075(in)X +3170(several)X +3431(aspects.)X +3736(The)X +3894(speci\256cation)X +555 2478(requires)N +840(that)X +985(the)X +1108(database)X +1410(keep)X +1587(redundant)X +1933(logs)X +2091(on)X +2196(different)X +2498(devices,)X +2784(but)X +2911(we)X +3030(use)X +3162(a)X +3223(single)X +3439(log.)X +3606(Furthermore,)X +4052(all)X +4157(tests)X +555 2568(were)N +734(run)X +863(on)X +965(a)X +1023(single,)X +1256(centralized)X +1631(system)X +1875(so)X +1968(there)X +2151(is)X +2226(no)X +2328(notion)X +2553(of)X +2641(remote)X +2885(accesses.)X +3219(Finally,)X +3486(we)X +3601(calculated)X +3948(throughput)X +555 2658(by)N +662(dividing)X +955(the)X +1080(total)X +1249(elapsed)X +1517(time)X +1686(by)X +1793(the)X +1918(number)X +2190(of)X +2284(transactions)X +2694(processed)X +3038(rather)X +3253(than)X +3418(by)X +3525(computing)X +3894(the)X +4018(response)X +555 2748(time)N +717(for)X +831(each)X +999(transaction.)X +755 2871(The)N +912(performance)X +1351(comparisons)X +1788(focus)X +1993(on)X +2104(traditional)X +2464(Unix)X +2655(techniques)X +3029(\(unprotected,)X +3486(using)X +3 f +3690(\257ock)X +1 f +3854(\(2\))X +3979(and)X +4126(using)X +3 f +555 2961(fsync)N +1 f +733(\(2\)\))X +884(and)X +1030(a)X +1096(commercial)X +1504(relational)X +1836(database)X +2142(system.)X +2433(Well-behaved)X +2913(applications)X +3329(using)X +3 f +3531(\257ock)X +1 f +3695(\(2\))X +3818(are)X +3946(guaranteed)X +555 3051(that)N +704(concurrent)X +1077(processes')X +1441(updates)X +1715(do)X +1824(not)X +1955(interact)X +2225(with)X +2396(one)X +2541(another,)X +2831(but)X +2962(no)X +3070(guarantees)X +3442(about)X +3648(atomicity)X +3978(are)X +4105(made.)X +555 3141(That)N +731(is,)X +833(if)X +911(the)X +1038(system)X +1289(crashes)X +1555(in)X +1646(mid-transaction,)X +2198(only)X +2369(parts)X +2554(of)X +2649(that)X +2797(transaction)X +3177(will)X +3329(be)X +3433(re\257ected)X +3738(in)X +3828(the)X +3954 0.3125(after-crash)AX +555 3231(state)N +725(of)X +815(the)X +936(database.)X +1276(The)X +1424(use)X +1554(of)X +3 f +1643(fsync)X +1 f +1821(\(2\))X +1937(at)X +2017(transaction)X +2391(commit)X +2657(time)X +2821(provides)X +3119(guarantees)X +3485(of)X +3574(durability)X +3907(after)X +4077(system)X +555 3321(failure.)N +825(However,)X +1160(there)X +1341(is)X +1414(no)X +1514(mechanism)X +1899(to)X +1981(perform)X +2260(transaction)X +2632(abort.)X +3 f +555 3507(5.1.1.)N +775(Single-User)X +1191(Tests)X +1 f +755 3630(These)N +978(tests)X +1151(compare)X +1459(LIBTP)X +1712(in)X +1804(a)X +1870(variety)X +2123(of)X +2220(con\256gurations)X +2708(to)X +2800(traditional)X +3159(UNIX)X +3390(solutions)X +3708(and)X +3854(a)X +3920(commercial)X +555 3720(relational)N +884(database)X +1187(system)X +1435(\(RDBMS\).)X +1814(To)X +1929(demonstrate)X +2347(the)X +2471(server)X +2694(architecture)X +3100(we)X +3220(built)X +3392(a)X +3454(front)X +3636(end)X +3777(test)X +3913(process)X +4179(that)X +555 3810(uses)N +732(TCL)X +922([OUST90])X +1304(to)X +1405(parse)X +1614(database)X +1930(access)X +2175(commands)X +2561(and)X +2716(call)X +2870(the)X +3006(database)X +3321(access)X +3565(routines.)X +3901(In)X +4006(one)X +4160(case)X +555 3900(\(SERVER\),)N +956(frontend)X +1249(and)X +1386(backend)X +1675(processes)X +2004(were)X +2181(created)X +2434(which)X +2650(communicated)X +3142(via)X +3260(an)X +3356(IP)X +3447(socket.)X +3712(In)X +3799(the)X +3917(second)X +4160(case)X +555 3990(\(TCL\),)N +802(a)X +860(single)X +1073(process)X +1336(read)X +1497(queries)X +1751(from)X +1929(standard)X +2223(input,)X +2429(parsed)X +2660(them,)X +2861(and)X +2998(called)X +3211(the)X +3330(database)X +3628(access)X +3855(routines.)X +4174(The)X +555 4080(performance)N +987(difference)X +1338(between)X +1630(the)X +1752(TCL)X +1927(and)X +2067(SERVER)X +2397(tests)X +2563(quanti\256es)X +2898(the)X +3020(communication)X +3542(overhead)X +3861(of)X +3952(the)X +4074(socket.)X +555 4170(The)N +732(RDBMS)X +1063(implementation)X +1617(used)X +1816(embedded)X +2198(SQL)X +2401(in)X +2515(C)X +2620(with)X +2814(stored)X +3062(database)X +3391(procedures.)X +3835(Therefore,)X +4224(its)X +555 4260(con\256guration)N +1003(is)X +1076(a)X +1132(hybrid)X +1361(of)X +1448(the)X +1566(single)X +1777(process)X +2038(architecture)X +2438(and)X +2574(the)X +2692(server)X +2909(architecture.)X +3349(The)X +3494(graph)X +3697(in)X +3779(\256gure)X +3986(six)X +4099(shows)X +555 4350(a)N +611(comparison)X +1005(of)X +1092(the)X +1210(following)X +1541(six)X +1654(con\256gurations:)X +1126 4506(LIBTP)N +1552(Uses)X +1728(the)X +1846(LIBTP)X +2088(library)X +2322(in)X +2404(a)X +2460(single)X +2671(application.)X +1126 4596(TCL)N +1552(Uses)X +1728(the)X +1846(LIBTP)X +2088(library)X +2322(in)X +2404(a)X +2460(single)X +2671(application,)X +3067(requires)X +3346(query)X +3549(parsing.)X +1126 4686(SERVER)N +1552(Uses)X +1728(the)X +1846(LIBTP)X +2088(library)X +2322(in)X +2404(a)X +2460(server)X +2677(con\256guration,)X +3144(requires)X +3423(query)X +3626(parsing.)X +1126 4776(NOTP)N +1552(Uses)X +1728(no)X +1828(locking,)X +2108(logging,)X +2392(or)X +2479(concurrency)X +2897(control.)X +1126 4866(FLOCK)N +1552(Uses)X +3 f +1728(\257ock)X +1 f +1892(\(2\))X +2006(for)X +2120(concurrency)X +2538(control)X +2785(and)X +2921(nothing)X +3185(for)X +3299(durability.)X +1126 4956(FSYNC)N +1552(Uses)X +3 f +1728(fsync)X +1 f +1906(\(2\))X +2020(for)X +2134(durability)X +2465(and)X +2601(nothing)X +2865(for)X +2979(concurrency)X +3397(control.)X +1126 5046(RDBMS)N +1552(Uses)X +1728(a)X +1784(commercial)X +2183(relational)X +2506(database)X +2803(system.)X +755 5235(The)N +902(results)X +1133(show)X +1324(that)X +1466(LIBTP,)X +1730(both)X +1894(in)X +1978(the)X +2098(procedural)X +2464(and)X +2602(parsed)X +2834(environments,)X +3312(is)X +3387(competitive)X +3787(with)X +3951(a)X +4009(commer-)X +555 5325(cial)N +692(system)X +935(\(comparing)X +1326(LIBTP,)X +1589(TCL,)X +1781(and)X +1917(RDBMS\).)X +2263(Compared)X +2617(to)X +2699(existing)X +2972(UNIX)X +3193(solutions,)X +3521(LIBTP)X +3763(is)X +3836(approximately)X +555 5415(15%)N +738(slower)X +988(than)X +1162(using)X +3 f +1371(\257ock)X +1 f +1535(\(2\))X +1665(or)X +1768(no)X +1884(protection)X +2245(but)X +2383(over)X +2562(80%)X +2745(better)X +2964(than)X +3137(using)X +3 f +3345(fsync)X +1 f +3523(\(2\))X +3652(\(comparing)X +4057(LIBTP,)X +555 5505(FLOCK,)N +857(NOTP,)X +1106(and)X +1242(FSYNC\).)X + +12 p +%%Page: 12 12 +10 s 10 xH 0 xS 1 f +3 f +8 s +3500 2184(RDBMS)N +1 Dt +3553 2085 MXY + 3553 2085 lineto + 3676 2085 lineto + 3676 1351 lineto + 3553 1351 lineto + 3553 2085 lineto +closepath 16 3553 1351 3676 2085 Dp +2018 2184(SERVER)N +1720 1168 MXY +0 917 Dl +122 0 Dl +0 -917 Dl +-122 0 Dl +1715 2184(TCL)N +2087 1534 MXY + 2087 1534 lineto + 2209 1534 lineto + 2209 2085 lineto + 2087 2085 lineto + 2087 1534 lineto +closepath 12 2087 1534 2209 2085 Dp +3187 MX + 3187 1534 lineto + 3309 1534 lineto + 3309 2085 lineto + 3187 2085 lineto + 3187 1534 lineto +closepath 19 3187 1534 3309 2085 Dp +3142 2184(FSYNC)N +2425(NOTP)X +2453 955 MXY + 2453 955 lineto + 2576 955 lineto + 2576 2085 lineto + 2453 2085 lineto + 2453 955 lineto +closepath 21 2453 955 2576 2085 Dp +2820 1000 MXY + 2820 1000 lineto + 2942 1000 lineto + 2942 2085 lineto + 2820 2085 lineto + 2820 1000 lineto +closepath 14 2820 1000 2942 2085 Dp +5 Dt +1231 2085 MXY +2567 0 Dl +4 Ds +1 Dt +1231 1840 MXY +2567 0 Dl +1231 1596 MXY +2567 0 Dl +1231 1351 MXY +2567 0 Dl +1231 1108 MXY +2567 0 Dl +1231 863 MXY +2567 0 Dl +11 s +1087 1877(2)N +1087 1633(4)N +1087 1388(6)N +1087 1145(8)N +1065 900(10)N +1028 763(TPS)N +-1 Ds +1353 2085 MXY + 1353 2085 lineto + 1353 1151 lineto + 1476 1151 lineto + 1476 2085 lineto + 1353 2085 lineto +closepath 3 1353 1151 1476 2085 Dp +8 s +1318 2184(LIBTP)N +2767(FLOCK)X +3 Dt +-1 Ds +10 s +1597 2399(Figure)N +1844(6:)X +1931(Single-User)X +2347(Performance)X +2814(Comparison.)X +1 f +10 f +555 2579(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +3 f +555 2855(5.1.2.)N +775(Multi-User)X +1174(Tests)X +1 f +755 2978(While)N +975(the)X +1097(single-user)X +1473(tests)X +1639(form)X +1819(a)X +1878(basis)X +2061(for)X +2178(comparing)X +2544(LIBTP)X +2789(to)X +2874(other)X +3062(systems,)X +3358(our)X +3488(goal)X +3649(in)X +3734(multi-user)X +4086(testing)X +555 3068(was)N +714(to)X +810(analyze)X +1089(its)X +1197(scalability.)X +1579(To)X +1701(this)X +1849(end,)X +2018(we)X +2145(have)X +2330(run)X +2470(the)X +2601(benchmark)X +2991(in)X +3086(three)X +3280(modes,)X +3542(the)X +3673(normal)X +3933(disk)X +4099(bound)X +555 3158(con\256guration)N +1010(\(\256gure)X +1252(seven\),)X +1510(a)X +1573(CPU)X +1755(bound)X +1982(con\256guration)X +2436(\(\256gure)X +2677(eight,)X +2884(READ-ONLY\),)X +3426(and)X +3569(lock)X +3734(contention)X +4099(bound)X +555 3248(\(\256gure)N +796(eight,)X +1003(NO_FSYNC\).)X +1510(Since)X +1715(the)X +1840(normal)X +2094(con\256guration)X +2548(is)X +2628(completely)X +3011(disk)X +3171(bound)X +3398(\(each)X +3600(transaction)X +3978(requires)X +4263(a)X +555 3354(random)N +823(read,)X +1005(a)X +1064(random)X +1332(write,)X +1540(and)X +1679(a)X +1738(sequential)X +2086(write)X +7 s +2251 3322(4)N +10 s +3354(\))Y +2329(we)X +2446(expect)X +2679(to)X +2764(see)X +2890(little)X +3059(performance)X +3489(improvement)X +3939(as)X +4028(the)X +4148(mul-)X +555 3444(tiprogramming)N +1064(level)X +1249(increases.)X +1613(In)X +1709(fact,)X +1879(\256gure)X +2095(seven)X +2307(reveals)X +2564(that)X +2713(we)X +2836(are)X +2964(able)X +3127(to)X +3218(overlap)X +3487(CPU)X +3670(and)X +3814(disk)X +3975(utilization)X +555 3534(slightly)N +825(producing)X +1181(approximately)X +1674(a)X +1740(10%)X +1917(performance)X +2354(improvement)X +2811(with)X +2983(two)X +3133(processes.)X +3511(After)X +3711(that)X +3861(point,)X +4075(perfor-)X +555 3624(mance)N +785(drops)X +983(off,)X +1117(and)X +1253(at)X +1331(a)X +1387(multi-programming)X +2038(level)X +2214(of)X +2301(4,)X +2381(we)X +2495(are)X +2614(performing)X +2995(worse)X +3207(than)X +3365(in)X +3447(the)X +3565(single)X +3776(process)X +4037(case.)X +755 3747(Similar)N +1021(behavior)X +1333(was)X +1489(reported)X +1787(on)X +1897(the)X +2025(commercial)X +2434(relational)X +2767(database)X +3074(system)X +3326(using)X +3529(the)X +3657(same)X +3852(con\256guration.)X +555 3837(The)N +707(important)X +1045(conclusion)X +1419(to)X +1508(draw)X +1696(from)X +1879(this)X +2021(is)X +2101(that)X +2248(you)X +2395(cannot)X +2636(attain)X +2841(good)X +3028(multi-user)X +3384(scaling)X +3638(on)X +3745(a)X +3808(badly)X +4013(balanced)X +555 3927(system.)N +839(If)X +915(multi-user)X +1266(performance)X +1695(on)X +1797(applications)X +2205(of)X +2293(this)X +2429(sort)X +2570(is)X +2644(important,)X +2996(one)X +3133(must)X +3309(have)X +3482(a)X +3539(separate)X +3824(logging)X +4089(device)X +555 4017(and)N +697(horizontally)X +1110(partition)X +1407(the)X +1531(database)X +1834(to)X +1921(allow)X +2124(a)X +2185(suf\256ciently)X +2570(high)X +2737(degree)X +2977(of)X +3069(multiprogramming)X +3698(that)X +3843(group)X +4055(commit)X +555 4107(can)N +687(amortize)X +988(the)X +1106(cost)X +1255(of)X +1342(log)X +1464(\257ushing.)X +755 4230(By)N +871(using)X +1067(a)X +1126(very)X +1292(small)X +1488(database)X +1788(\(one)X +1954(that)X +2097(can)X +2232(be)X +2331(entirely)X +2599(cached)X +2846(in)X +2930(main)X +3112(memory\))X +3428(and)X +3566(read-only)X +3896(transactions,)X +555 4320(we)N +670(generated)X +1004(a)X +1061(CPU)X +1236(bound)X +1456(environment.)X +1921(By)X +2034(using)X +2227(the)X +2345(same)X +2530(small)X +2723(database,)X +3040(the)X +3158(complete)X +3472(TPCB)X +3691(transaction,)X +4083(and)X +4219(no)X +3 f +555 4410(fsync)N +1 f +733(\(2\))X +862(on)X +977(the)X +1110(log)X +1247(at)X +1340(commit,)X +1639(we)X +1768(created)X +2036(a)X +2107(lock)X +2280(contention)X +2652(bound)X +2886(environment.)X +3365(The)X +3524(small)X +3731(database)X +4042(used)X +4223(an)X +555 4500(account)N +828(\256le)X +953(containing)X +1314(only)X +1479(1000)X +1662(records)X +1922(rather)X +2133(than)X +2294(the)X +2415(full)X +2549(1,000,000)X +2891(records)X +3150(and)X +3288(ran)X +3413(enough)X +3671(transactions)X +4076(to)X +4160(read)X +555 4590(the)N +677(entire)X +883(database)X +1183(into)X +1330(the)X +1451(buffer)X +1671(pool)X +1836(\(2000\))X +2073(before)X +2302(beginning)X +2645(measurements.)X +3147(The)X +3295(read-only)X +3626(transaction)X +4001(consisted)X +555 4680(of)N +646(three)X +831(database)X +1132(reads)X +1326(\(from)X +1533(the)X +1655(1000)X +1839(record)X +2069(account)X +2343(\256le,)X +2489(the)X +2611(100)X +2754(record)X +2983(teller)X +3171(\256le,)X +3316(and)X +3455(the)X +3576(10)X +3679(record)X +3908(branch)X +4150(\256le\).)X +555 4770(Since)N +759(no)X +865(data)X +1025(were)X +1208(modi\256ed)X +1518(and)X +1660(no)X +1766(history)X +2014(records)X +2277(were)X +2460(written,)X +2733(no)X +2839(log)X +2966(records)X +3228(were)X +3410(written.)X +3702(For)X +3838(the)X +3961(contention)X +555 4860(bound)N +780(con\256guration,)X +1252(we)X +1371(used)X +1543(the)X +1666(normal)X +1918(TPCB)X +2142(transaction)X +2519(\(against)X +2798(the)X +2920(small)X +3117(database\))X +3445(and)X +3585(disabled)X +3876(the)X +3998(log)X +4124(\257ush.)X +555 4950(Figure)N +784(eight)X +964(shows)X +1184(both)X +1346(of)X +1433(these)X +1618(results.)X +755 5073(The)N +902(read-only)X +1231(test)X +1363(indicates)X +1669(that)X +1810(we)X +1925(barely)X +2147(scale)X +2329(at)X +2408(all)X +2509(in)X +2592(the)X +2711(CPU)X +2887(bound)X +3108(case.)X +3308(The)X +3454(explanation)X +3849(for)X +3964(that)X +4105(is)X +4179(that)X +555 5163(even)N +735(with)X +905(a)X +969(single)X +1188(process,)X +1477(we)X +1599(are)X +1726(able)X +1888(to)X +1978(drive)X +2171(the)X +2297(CPU)X +2480(utilization)X +2832(to)X +2922(96%.)X +3137(As)X +3254(a)X +3317(result,)X +3542(that)X +3689(gives)X +3885(us)X +3983(very)X +4153(little)X +555 5253(room)N +753(for)X +876(improvement,)X +1352(and)X +1497(it)X +1570(takes)X +1764(a)X +1829(multiprogramming)X +2462(level)X +2647(of)X +2743(four)X +2906(to)X +2997(approach)X +3321(100%)X +3537(CPU)X +3721(saturation.)X +4106(In)X +4201(the)X +555 5343(case)N +718(where)X +939(we)X +1057(do)X +1161(perform)X +1444(writes,)X +1684(we)X +1802(are)X +1925(interested)X +2261(in)X +2347(detecting)X +2665(when)X +2863(lock)X +3025(contention)X +3387(becomes)X +3691(a)X +3750(dominant)X +4075(perfor-)X +555 5433(mance)N +787(factor.)X +1037(Contention)X +1414(will)X +1560(cause)X +1761(two)X +1903(phenomena;)X +2317(we)X +2433(will)X +2579(see)X +2704(transactions)X +3109(queueing)X +3425(behind)X +3665(frequently)X +4017(accessed)X +555 5523(data,)N +731(and)X +869(we)X +985(will)X +1131(see)X +1256(transaction)X +1629(abort)X +1815(rates)X +1988(increasing)X +2339(due)X +2476(to)X +2559(deadlock.)X +2910(Given)X +3127(that)X +3268(the)X +3387(branch)X +3627(\256le)X +3750(contains)X +4038(only)X +4201(ten)X +8 s +10 f +555 5595(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)N +5 s +1 f +727 5673(4)N +8 s +763 5698(Although)N +1021(the)X +1115(log)X +1213(is)X +1272(written)X +1469(sequentially,)X +1810(we)X +1900(do)X +1980(not)X +2078(get)X +2172(the)X +2266(bene\256t)X +2456(of)X +2525(sequentiality)X +2868(since)X +3015(the)X +3109(log)X +3207(and)X +3315(database)X +3550(reside)X +3718(on)X +3798(the)X +3892(same)X +4039(disk.)X + +13 p +%%Page: 13 13 +8 s 8 xH 0 xS 1 f +10 s +3 f +1 f +3187 2051 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3286 2028 MXY +0 17 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3384 1926 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3483 1910 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3581 1910 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3680 1832 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3778 1909 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3877 1883 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3975 1679 MXY +0 17 Dl +0 -8 Dl +9 0 Dl +-18 0 Dl +4074 1487 MXY +0 17 Dl +0 -8 Dl +9 0 Dl +-18 0 Dl +5 Dt +3187 2060 MXY +99 -24 Dl +98 -101 Dl +99 -16 Dl +98 0 Dl +99 -78 Dl +98 77 Dl +99 -26 Dl +98 -204 Dl +99 -192 Dl +3 f +6 s +4088 1516(SMALL)N +3 Dt +3187 2051 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3286 2051 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3384 2041 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3483 1990 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3581 1843 MXY +0 17 Dl +0 -8 Dl +9 0 Dl +-18 0 Dl +3680 1578 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3778 1496 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3877 1430 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +3975 1269 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +4074 1070 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1 Dt +3187 2060 MXY +99 0 Dl +98 -10 Dl +99 -51 Dl +98 -147 Dl +99 -265 Dl +98 -82 Dl +99 -66 Dl +98 -161 Dl +99 -199 Dl +4088 1099(LARGE)N +5 Dt +3089 2060 MXY +985 0 Dl +3089 MX +0 -1174 Dl +4 Ds +1 Dt +3581 2060 MXY +0 -1174 Dl +4074 2060 MXY +0 -1174 Dl +3089 1825 MXY +985 0 Dl +9 s +2993 1855(25)N +3089 1591 MXY +985 0 Dl +2993 1621(50)N +3089 1356 MXY +985 0 Dl +2993 1386(75)N +3089 1121 MXY +985 0 Dl +2957 1151(100)N +3089 886 MXY +985 0 Dl +2957 916(125)N +3281 2199(Multiprogramming)N +3071 2152(0)N +3569(5)X +4038(10)X +2859 787(Aborts)N +3089(per)X +3211(500)X +2901 847(transactions)N +-1 Ds +3 Dt +2037 1342 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2125 1358 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2213 1341 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2301 1191 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2388 1124 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-17 0 Dl +2476 1157 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2564 1157 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2652 1161 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2740 1153 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2828 1150 MXY +0 18 Dl +0 -9 Dl +8 0 Dl +-17 0 Dl +5 Dt +2037 1351 MXY +88 16 Dl +88 -17 Dl +88 -150 Dl +87 -67 Dl +88 33 Dl +88 0 Dl +88 4 Dl +88 -8 Dl +88 -3 Dl +6 s +2685 1234(READ-ONLY)N +3 Dt +2037 1464 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2125 1640 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2213 1854 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2301 1872 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2388 1871 MXY +0 17 Dl +0 -9 Dl +9 0 Dl +-17 0 Dl +2476 1933 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2564 1914 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2652 1903 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2740 1980 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +2828 2004 MXY +0 18 Dl +0 -9 Dl +8 0 Dl +-17 0 Dl +1 Dt +2037 1473 MXY +88 176 Dl +88 214 Dl +88 18 Dl +87 -2 Dl +88 63 Dl +88 -19 Dl +88 -11 Dl +88 77 Dl +88 24 Dl +2759 1997(NO-FSYNC)N +5 Dt +1949 2060 MXY +879 0 Dl +1949 MX +0 -1174 Dl +4 Ds +1 Dt +2388 2060 MXY +0 -1174 Dl +2828 2060 MXY +0 -1174 Dl +1949 1825 MXY +879 0 Dl +9 s +1842 1855(40)N +1949 1591 MXY +879 0 Dl +1842 1621(80)N +1949 1356 MXY +879 0 Dl +1806 1386(120)N +1949 1121 MXY +879 0 Dl +1806 1151(160)N +1949 886 MXY +879 0 Dl +1806 916(200)N +2088 2199(Multiprogramming)N +1844 863(in)N +1922(TPS)X +1761 792(Throughput)N +1931 2121(0)N +2370 2133(5)N +2792(10)X +6 s +1679 1833(LIBTP)N +-1 Ds +3 Dt +837 1019 MXY +0 17 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +929 878 MXY +0 17 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1021 939 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1113 1043 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1205 1314 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1297 1567 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1389 1665 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1481 1699 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1573 1828 MXY +0 18 Dl +0 -9 Dl +9 0 Dl +-18 0 Dl +1665 1804 MXY +0 18 Dl +0 -9 Dl +8 0 Dl +-17 0 Dl +5 Dt +837 1027 MXY +92 -141 Dl +92 62 Dl +92 104 Dl +92 271 Dl +92 253 Dl +92 98 Dl +92 34 Dl +92 129 Dl +92 -24 Dl +745 2060 MXY +920 0 Dl +745 MX +0 -1174 Dl +4 Ds +1 Dt +1205 2060 MXY +0 -1174 Dl +1665 2060 MXY +0 -1174 Dl +745 1766 MXY +920 0 Dl +9 s +673 1796(3)N +745 1473 MXY +920 0 Dl +673 1503(5)N +745 1180 MXY +920 0 Dl +673 1210(8)N +745 886 MXY +920 0 Dl +637 916(10)N +905 2199(Multiprogramming)N +622 851(in)N +700(TPS)X +575 792(Throughput)N +733 2152(0)N +1196(5)X +1629(10)X +3 Dt +-1 Ds +8 s +655 2441(Figure)N +872(7:)X +960(Multi-user)X +1286(Performance.)X +1 f +655 2531(Since)N +825(the)X +931(con\256guration)X +1300(is)X +1371(completely)X +655 2621(disk)N +790(bound,)X +994(we)X +1096(see)X +1204(only)X +1345(a)X +1400(small)X +1566(im-)X +655 2711(provement)N +964(by)X +1064(adding)X +1274(a)X +1337(second)X +1549(pro-)X +655 2801(cess.)N +849(Adding)X +1081(any)X +1213(more)X +1383(concurrent)X +655 2891(processes)N +935(causes)X +1137(performance)X +1493(degra-)X +655 2981(dation.)N +3 f +1927 2441(Figure)N +2149(8:)X +2243(Multi-user)X +2574(Performance)X +1927 2531(on)N +2021(a)X +2079(small)X +2251(database.)X +1 f +2551(With)X +2704(one)X +2821(pro-)X +1927 2621(cess,)N +2075(we)X +2174(are)X +2276(driving)X +2486(the)X +2589(CPU)X +2739(at)X +2810(96%)X +1927 2711(utilization)N +2215(leaving)X +2430(little)X +2575(room)X +2737(for)X +2838(im-)X +1927 2801(provement)N +2238(as)X +2328(the)X +2443(multiprogramming)X +1927 2891(level)N +2091(increases.)X +2396(In)X +2489(the)X +2607(NO-FSYNC)X +1927 2981(case,)N +2076(lock)X +2209(contention)X +2502(degrades)X +2751(perfor-)X +1927 3071(mance)N +2117(as)X +2194(soon)X +2339(as)X +2416(a)X +2468(second)X +2669(process)X +2884(is)X +1927 3161(added.)N +3 f +3199 2441(Figure)N +3405(9:)X +3482(Abort)X +3669(rates)X +3827(on)X +3919(the)X +4028(TPCB)X +3199 2531(Benchmark.)N +1 f +3589(The)X +3726(abort)X +3895(rate)X +4028(climbs)X +3199 2621(more)N +3366(quickly)X +3594(for)X +3704(the)X +3818(large)X +3980(database)X +3199 2711(test)N +3324(since)X +3491(processes)X +3771(are)X +3884(descheduled)X +3199 2801(more)N +3409(frequently,)X +3766(allowing)X +4068(more)X +3199 2891(processes)N +3459(to)X +3525(vie)X +3619(for)X +3709(the)X +3803(same)X +3950(locks.)X +10 s +10 f +555 3284(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +1 f +555 3560(records,)N +835(we)X +952(expect)X +1185(contention)X +1546(to)X +1631(become)X +1904(a)X +1963(factor)X +2174(quickly)X +2437(and)X +2576(the)X +2697(NO-FSYNC)X +3120(line)X +3263(in)X +3348(\256gure)X +3557(eight)X +3739(demonstrates)X +4184(this)X +555 3650(dramatically.)N +1022(Each)X +1209(additional)X +1555(process)X +1822(causes)X +2058(both)X +2226(more)X +2417(waiting)X +2682(and)X +2823(more)X +3013(deadlocking.)X +3470(Figure)X +3704(nine)X +3867(shows)X +4092(that)X +4237(in)X +555 3740(the)N +681(small)X +882(database)X +1187(case)X +1353(\(SMALL\),)X +1725(waiting)X +1992(is)X +2072(the)X +2197(dominant)X +2526(cause)X +2732(of)X +2826(declining)X +3151(performance)X +3585(\(the)X +3737(number)X +4009(of)X +4103(aborts)X +555 3830(increases)N +878(less)X +1026(steeply)X +1281(than)X +1447(the)X +1573(performance)X +2008(drops)X +2214(off)X +2336(in)X +2426(\256gure)X +2641(eight\),)X +2876(while)X +3082(in)X +3172(the)X +3298(large)X +3487(database)X +3792(case)X +3958(\(LARGE\),)X +555 3920(deadlocking)N +967(contributes)X +1343(more)X +1528(to)X +1610(the)X +1728(declining)X +2046(performance.)X +755 4043(Deadlocks)N +1116(are)X +1237(more)X +1424(likely)X +1628(to)X +1712(occur)X +1913(in)X +1997(the)X +2116(LARGE)X +2404(test)X +2536(than)X +2695(in)X +2778(the)X +2897(SMALL)X +3189(test)X +3321(because)X +3597(there)X +3779(are)X +3899(more)X +4085(oppor-)X +555 4133(tunities)N +814(to)X +900(wait.)X +1082(In)X +1173(the)X +1295(SMALL)X +1590(case,)X +1773(processes)X +2105(never)X +2307(do)X +2410(I/O)X +2540(and)X +2679(are)X +2801(less)X +2944(likely)X +3149(to)X +3234(be)X +3333(descheduled)X +3753(during)X +3985(a)X +4044(transac-)X +555 4223(tion.)N +740(In)X +828(the)X +947(LARGE)X +1235(case,)X +1415(processes)X +1744(will)X +1889(frequently)X +2240(be)X +2337(descheduled)X +2755(since)X +2941(they)X +3100(have)X +3273(to)X +3356(perform)X +3636(I/O.)X +3804(This)X +3967(provides)X +4263(a)X +555 4313(window)N +837(where)X +1058(a)X +1118(second)X +1365(process)X +1630(can)X +1766(request)X +2022(locks)X +2215(on)X +2318(already)X +2578(locked)X +2815(pages,)X +3041(thus)X +3197(increasing)X +3550(the)X +3671(likelihood)X +4018(of)X +4108(build-)X +555 4403(ing)N +677(up)X +777(long)X +939(chains)X +1164(of)X +1251(waiting)X +1511(processes.)X +1879(Eventually,)X +2266(this)X +2401(leads)X +2586(to)X +2668(deadlock.)X +3 f +555 4589(5.2.)N +715(The)X +868(OO1)X +1052(Benchmark)X +1 f +755 4712(The)N +903(TPCB)X +1125(benchmark)X +1505(described)X +1836(in)X +1921(the)X +2042(previous)X +2341(section)X +2591(measures)X +2913(performance)X +3343(under)X +3549(a)X +3608(conventional)X +4044(transac-)X +555 4802(tion)N +706(processing)X +1076(workload.)X +1446(Other)X +1656(application)X +2039(domains,)X +2357(such)X +2531(as)X +2625(computer-aided)X +3156(design,)X +3412(have)X +3591(substantially)X +4022(different)X +555 4892(access)N +786(patterns.)X +1105(In)X +1197(order)X +1392(to)X +1479(measure)X +1772(the)X +1895(performance)X +2327(of)X +2418(LIBTP)X +2664(under)X +2871(workloads)X +3229(of)X +3320(this)X +3459(type,)X +3641(we)X +3759(implemented)X +4201(the)X +555 4982(OO1)N +731(benchmark)X +1108(described)X +1436(in)X +1518([CATT91].)X +755 5105(The)N +908(database)X +1213(models)X +1472(a)X +1535(set)X +1651(of)X +1745(electronics)X +2120(components)X +2534(with)X +2703(connections)X +3113(among)X +3358(them.)X +3585(One)X +3746(table)X +3929(stores)X +4143(parts)X +555 5195(and)N +696(another)X +962(stores)X +1174(connections.)X +1622(There)X +1835(are)X +1959(three)X +2145(connections)X +2552(originating)X +2927(at)X +3009(any)X +3149(given)X +3351(part.)X +3540(Ninety)X +3782(percent)X +4043(of)X +4134(these)X +555 5285(connections)N +960(are)X +1081(to)X +1165(nearby)X +1406(parts)X +1584(\(those)X +1802(with)X +1966(nearby)X +2 f +2207(ids)X +1 f +2300(\))X +2348(to)X +2431(model)X +2652(the)X +2771(spatial)X +3001(locality)X +3262(often)X +3448(exhibited)X +3767(in)X +3850(CAD)X +4040(applica-)X +555 5375(tions.)N +779(Ten)X +933(percent)X +1198(of)X +1293(the)X +1419(connections)X +1830(are)X +1957(randomly)X +2292(distributed)X +2662(among)X +2908(all)X +3016(other)X +3209(parts)X +3393(in)X +3483(the)X +3609(database.)X +3954(Every)X +4174(part)X +555 5465(appears)N +829(exactly)X +1089(three)X +1278(times)X +1479(in)X +1569(the)X +2 f +1695(from)X +1 f +1874(\256eld)X +2043(of)X +2137(a)X +2200(connection)X +2579(record,)X +2832(and)X +2975(zero)X +3141(or)X +3235(more)X +3427(times)X +3627(in)X +3716(the)X +2 f +3841(to)X +1 f +3930(\256eld.)X +4139(Parts)X +555 5555(have)N +2 f +727(x)X +1 f +783(and)X +2 f +919(y)X +1 f +975(locations)X +1284(set)X +1393(randomly)X +1720(in)X +1802(an)X +1898(appropriate)X +2284(range.)X + +14 p +%%Page: 14 14 +10 s 10 xH 0 xS 1 f +3 f +1 f +755 630(The)N +900(intent)X +1102(of)X +1189(OO1)X +1365(is)X +1438(to)X +1520(measure)X +1808(the)X +1926(overall)X +2169(cost)X +2318(of)X +2405(a)X +2461(query)X +2664(mix)X +2808(characteristic)X +3257(of)X +3344(engineering)X +3743(database)X +4040(applica-)X +555 720(tions.)N +770(There)X +978(are)X +1097(three)X +1278(tests:)X +10 f +635 843(g)N +2 f +755(Lookup)X +1 f +1022(generates)X +1353(1,000)X +1560(random)X +1832(part)X +2 f +1984(ids)X +1 f +2077(,)X +2124(fetches)X +2378(the)X +2502(corresponding)X +2987(parts)X +3169(from)X +3351(the)X +3475(database,)X +3798(and)X +3940(calls)X +4113(a)X +4175(null)X +755 933(procedure)N +1097(in)X +1179(the)X +1297(host)X +1450(programming)X +1906(language)X +2216(with)X +2378(the)X +2496(parts')X +2 f +2699(x)X +1 f +2755(and)X +2 f +2891(y)X +1 f +2947(positions.)X +10 f +635 1056(g)N +2 f +755(Traverse)X +1 f +1067(retrieves)X +1371(a)X +1434(random)X +1706(part)X +1858(from)X +2041(the)X +2166(database)X +2470(and)X +2613(follows)X +2880(connections)X +3290(from)X +3473(it)X +3544(to)X +3632(other)X +3823(parts.)X +4045(Each)X +4232(of)X +755 1146(those)N +947(parts)X +1126(is)X +1202(retrieved,)X +1531(and)X +1670(all)X +1773(connections)X +2179(from)X +2358(it)X +2424(followed.)X +2771(This)X +2935(procedure)X +3279(is)X +3354(repeated)X +3649(depth-\256rst)X +4000(for)X +4116(seven)X +755 1236(hops)N +930(from)X +1110(the)X +1232(original)X +1505(part,)X +1674(for)X +1792(a)X +1852(total)X +2018(of)X +2109(3280)X +2293(parts.)X +2513(Backward)X +2862(traversal)X +3162(also)X +3314(exists,)X +3539(and)X +3678(follows)X +3941(all)X +4044(connec-)X +755 1326(tions)N +930(into)X +1074(a)X +1130(given)X +1328(part)X +1473(to)X +1555(their)X +1722(origin.)X +10 f +635 1449(g)N +2 f +755(Insert)X +1 f +962(adds)X +1129(100)X +1269(new)X +1423(parts)X +1599(and)X +1735(their)X +1902(connections.)X +755 1572(The)N +913(benchmark)X +1303(is)X +1389(single-user,)X +1794(but)X +1929(multi-user)X +2291(access)X +2530(controls)X +2821(\(locking)X +3120(and)X +3268(transaction)X +3652(protection\))X +4036(must)X +4223(be)X +555 1662(enforced.)N +898(It)X +968(is)X +1042(designed)X +1348(to)X +1431(be)X +1528(run)X +1656(on)X +1757(a)X +1814(database)X +2112(with)X +2275(20,000)X +2516(parts,)X +2713(and)X +2850(on)X +2951(one)X +3087(with)X +3249(200,000)X +3529(parts.)X +3745(Because)X +4033(we)X +4147(have)X +555 1752(insuf\256cient)N +935(disk)X +1088(space)X +1287(for)X +1401(the)X +1519(larger)X +1727(database,)X +2044(we)X +2158(report)X +2370(results)X +2599(only)X +2761(for)X +2875(the)X +2993(20,000)X +3233(part)X +3378(database.)X +3 f +555 1938(5.2.1.)N +775(Implementation)X +1 f +755 2061(The)N +920(LIBTP)X +1182(implementation)X +1724(of)X +1831(OO1)X +2027(uses)X +2205(the)X +2342(TCL)X +2532([OUST90])X +2914(interface)X +3235(described)X +3582(earlier.)X +3867(The)X +4031(backend)X +555 2151(accepts)N +813(commands)X +1181(over)X +1345(an)X +1442(IP)X +1534(socket)X +1760(and)X +1897(performs)X +2208(the)X +2327(requested)X +2656(database)X +2954(actions.)X +3242(The)X +3387(frontend)X +3679(opens)X +3886(and)X +4022(executes)X +555 2241(a)N +618(TCL)X +796(script.)X +1041(This)X +1210(script)X +1415(contains)X +1709(database)X +2013(accesses)X +2313(interleaved)X +2697(with)X +2866(ordinary)X +3165(program)X +3463(control)X +3716(statements.)X +4120(Data-)X +555 2331(base)N +718(commands)X +1085(are)X +1204(submitted)X +1539(to)X +1621(the)X +1739(backend)X +2027(and)X +2163(results)X +2392(are)X +2511(bound)X +2731(to)X +2813(program)X +3105(variables.)X +755 2454(The)N +903(parts)X +1082(table)X +1261(was)X +1409(stored)X +1628(as)X +1718(a)X +1776(B-tree)X +1999(indexed)X +2275(by)X +2 f +2377(id)X +1 f +2439(.)X +2501(The)X +2648(connection)X +3022(table)X +3200(was)X +3347(stored)X +3565(as)X +3654(a)X +3712(set)X +3823(of)X +3912(\256xed-length)X +555 2544(records)N +824(using)X +1029(the)X +1159(4.4BSD)X +1446(recno)X +1657(access)X +1895(method.)X +2207(In)X +2306(addition,)X +2620(two)X +2771(B-tree)X +3003(indices)X +3261(were)X +3449(maintained)X +3836(on)X +3947(connection)X +555 2634(table)N +732(entries.)X +1007(One)X +1162(index)X +1360(mapped)X +1634(the)X +2 f +1752(from)X +1 f +1923(\256eld)X +2085(to)X +2167(a)X +2223(connection)X +2595(record)X +2821(number,)X +3106(and)X +3242(the)X +3360(other)X +3545(mapped)X +3819(the)X +2 f +3937(to)X +1 f +4019(\256eld)X +4181(to)X +4263(a)X +555 2724(connection)N +932(record)X +1163(number.)X +1473(These)X +1690(indices)X +1941(support)X +2205(fast)X +2345(lookups)X +2622(on)X +2726(connections)X +3133(in)X +3219(both)X +3385(directions.)X +3765(For)X +3900(the)X +4022(traversal)X +555 2814(tests,)N +743(the)X +867(frontend)X +1165(does)X +1338(an)X +1439(index)X +1642(lookup)X +1889(to)X +1976(discover)X +2273(the)X +2396(connected)X +2747(part's)X +2 f +2955(id)X +1 f +3017(,)X +3062(and)X +3203(then)X +3366(does)X +3538(another)X +3804(lookup)X +4051(to)X +4138(fetch)X +555 2904(the)N +673(part)X +818(itself.)X +3 f +555 3090(5.2.2.)N +775(Performance)X +1242(Measurements)X +1766(for)X +1889(OO1)X +1 f +755 3213(We)N +888(compare)X +1186(LIBTP's)X +1487(OO1)X +1664(performance)X +2092(to)X +2174(that)X +2314(reported)X +2602(in)X +2684([CATT91].)X +3087(Those)X +3303(results)X +3532(were)X +3709(collected)X +4019(on)X +4119(a)X +4175(Sun)X +555 3303(3/280)N +759(\(25)X +888(MHz)X +1075(MC68020\))X +1448(with)X +1612(16)X +1714(MBytes)X +1989(of)X +2078(memory)X +2367(and)X +2505(two)X +2647(Hitachi)X +2904(892MByte)X +3267(disks)X +3452(\(15)X +3580(ms)X +3694(average)X +3966(seek)X +4130(time\))X +555 3393(behind)N +793(an)X +889(SMD-4)X +1149(controller.)X +1521(Frontends)X +1861(ran)X +1984(on)X +2084(an)X +2180(8MByte)X +2462(Sun)X +2606(3/260.)X +755 3516(In)N +844(order)X +1036(to)X +1120(measure)X +1410(performance)X +1839(on)X +1941(a)X +1999(machine)X +2293(of)X +2382(roughly)X +2653(equivalent)X +3009(processor)X +3339(power,)X +3582(we)X +3698(ran)X +3822(one)X +3959(set)X +4069(of)X +4157(tests)X +555 3606(on)N +666(a)X +733(standalone)X +1107(MC68030-based)X +1671(HP300)X +1923(\(33MHz)X +2225(MC68030\).)X +2646(The)X +2801(database)X +3108(was)X +3263(stored)X +3489(on)X +3599(a)X +3665(300MByte)X +4037(HP7959)X +555 3696(SCSI)N +744(disk)X +898(\(17)X +1026(ms)X +1139(average)X +1410(seek)X +1573(time\).)X +1802(Since)X +2000(this)X +2135(machine)X +2427(is)X +2500(not)X +2622(connected)X +2968(to)X +3050(a)X +3106(network,)X +3409(we)X +3523(ran)X +3646(local)X +3822(tests)X +3984(where)X +4201(the)X +555 3786(frontend)N +855(and)X +999(backend)X +1295(run)X +1430(on)X +1538(the)X +1664(same)X +1856(machine.)X +2195(We)X +2334(compare)X +2638(these)X +2830(measurements)X +3316(with)X +3485(Cattell's)X +3783(local)X +3966(Sun)X +4117(3/280)X +555 3876(numbers.)N +755 3999(Because)N +1051(the)X +1177(benchmark)X +1562(requires)X +1849(remote)X +2100(access,)X +2354(we)X +2476(ran)X +2607(another)X +2876(set)X +2993(of)X +3088(tests)X +3258(on)X +3365(a)X +3428(DECstation)X +3828(5000/200)X +4157(with)X +555 4089(32M)N +732(of)X +825(memory)X +1118(running)X +1393(Ultrix)X +1610(V4.0)X +1794(and)X +1936(a)X +1998(DEC)X +2184(1GByte)X +2459(RZ57)X +2666(SCSI)X +2859(disk.)X +3057(We)X +3194(compare)X +3496(the)X +3619(local)X +3800(performance)X +4232(of)X +555 4179(OO1)N +734(on)X +837(the)X +958(DECstation)X +1354(to)X +1439(its)X +1536(remote)X +1781(performance.)X +2250(For)X +2383(the)X +2503(remote)X +2748(case,)X +2929(we)X +3045(ran)X +3170(the)X +3290(frontend)X +3584(on)X +3686(a)X +3744(DECstation)X +4139(3100)X +555 4269(with)N +717(16)X +817(MBytes)X +1090(of)X +1177(main)X +1357(memory.)X +755 4392(The)N +900(databases)X +1228(tested)X +1435(in)X +1517([CATT91])X +1880(are)X +10 f +635 4515(g)N +1 f +755(INDEX,)X +1045(a)X +1101(highly-optimized)X +1672(access)X +1898(method)X +2158(package)X +2442(developed)X +2792(at)X +2870(Sun)X +3014(Microsystems.)X +10 f +635 4638(g)N +1 f +755(OODBMS,)X +1137(a)X +1193(beta)X +1347(release)X +1591(of)X +1678(a)X +1734(commercial)X +2133(object-oriented)X +2639(database)X +2936(management)X +3366(system.)X +10 f +635 4761(g)N +1 f +755(RDBMS,)X +1076(a)X +1133(UNIX-based)X +1565(commercial)X +1965(relational)X +2289(data)X +2444(manager)X +2742(at)X +2821(production)X +3189(release.)X +3474(The)X +3620(OO1)X +3797(implementation)X +755 4851(used)N +922(embedded)X +1272(SQL)X +1443(in)X +1525(C.)X +1638(Stored)X +1867(procedures)X +2240(were)X +2417(de\256ned)X +2673(to)X +2755(reduce)X +2990(client-server)X +3412(traf\256c.)X +755 4974(Table)N +974(two)X +1130(shows)X +1366(the)X +1500(measurements)X +1995(from)X +2187([CATT91])X +2566(and)X +2718(LIBTP)X +2976(for)X +3106(a)X +3178(local)X +3370(test)X +3517(on)X +3632(the)X +3765(MC680x0-based)X +555 5064(hardware.)N +915(All)X +1037(caches)X +1272(are)X +1391(cleared)X +1644(before)X +1870(each)X +2038(test.)X +2209(All)X +2331(times)X +2524(are)X +2643(in)X +2725(seconds.)X +755 5187(Table)N +960(two)X +1102(shows)X +1324(that)X +1466(LIBTP)X +1710(outperforms)X +2123(the)X +2242(commercial)X +2642(relational)X +2966(system,)X +3229(but)X +3352(is)X +3426(slower)X +3661(than)X +3820(OODBMS)X +4183(and)X +555 5277(INDEX.)N +872(Since)X +1077(the)X +1202(caches)X +1444(were)X +1628(cleared)X +1888(at)X +1973(the)X +2098(start)X +2263(of)X +2356(each)X +2530(test,)X +2687(disk)X +2846(throughput)X +3223(is)X +3302(critical)X +3551(in)X +3639(this)X +3780(test.)X +3957(The)X +4108(single)X +555 5367(SCSI)N +749(HP)X +877(drive)X +1068(used)X +1241(by)X +1347(LIBTP)X +1595(is)X +1674(approximately)X +2163(13%)X +2336(slower)X +2576(than)X +2739(the)X +2862(disks)X +3051(used)X +3223(in)X +3310([CATT91])X +3678(which)X +3899(accounts)X +4205(for)X +555 5457(part)N +700(of)X +787(the)X +905(difference.)X +755 5580(OODBMS)N +1118(and)X +1255(INDEX)X +1525(outperform)X +1906(LIBTP)X +2148(most)X +2323(dramatically)X +2744(on)X +2844(traversal.)X +3181(This)X +3343(is)X +3416(because)X +3691(we)X +3805(use)X +3932(index)X +4130(look-)X +555 5670(ups)N +689(to)X +774(\256nd)X +921(connections,)X +1347(whereas)X +1634(the)X +1755(other)X +1942(two)X +2084(systems)X +2359(use)X +2488(a)X +2546(link)X +2692(access)X +2920(method.)X +3222(The)X +3369(index)X +3569(requires)X +3850(us)X +3943(to)X +4027(examine)X + +15 p +%%Page: 15 15 +10 s 10 xH 0 xS 1 f +3 f +1 f +10 f +555 679(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)N +2 f +606 769(Measure)N +1 f +1019(INDEX)X +1389(OODBMS)X +1851(RDBMS)X +2250(LIBTP)X +10 f +555 771(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)N +555 787(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)N +1 f +595 869(Lookup)N +1114(5.4)X +1490(12.9)X +1950(27)X +2291(27.2)X +595 959(Traversal)N +1074(13)X +1530(9.8)X +1950(90)X +2291(47.3)X +595 1049(Insert)N +1114(7.4)X +1530(1.5)X +1950(22)X +2331(9.7)X +10 f +555 1059(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)N +555(c)X +999(c)Y +919(c)Y +839(c)Y +759(c)Y +959 1059(c)N +999(c)Y +919(c)Y +839(c)Y +759(c)Y +1329 1059(c)N +999(c)Y +919(c)Y +839(c)Y +759(c)Y +1791 1059(c)N +999(c)Y +919(c)Y +839(c)Y +759(c)Y +2190 1059(c)N +999(c)Y +919(c)Y +839(c)Y +759(c)Y +2512 1059(c)N +999(c)Y +919(c)Y +839(c)Y +759(c)Y +2618 679(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2 f +2829 769(Measure)N +3401(Cache)X +3726(Local)X +4028(Remote)X +1 f +10 f +2618 771(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2618 787(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2658 869(Lookup)N +3401(cold)X +3747(15.7)X +4078(20.6)X +3401 959(warm)N +3787(7.8)X +4078(12.4)X +10 f +2618 969(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2658 1059(Forward)N +2950(traversal)X +3401(cold)X +3747(28.4)X +4078(52.6)X +3401 1149(warm)N +3747(23.5)X +4078(47.4)X +10 f +2618 1159(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2658 1249(Backward)N +3004(traversal)X +3401(cold)X +3747(24.2)X +4078(47.4)X +3401 1339(warm)N +3747(24.3)X +4078(47.6)X +10 f +2618 1349(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +1 f +2658 1439(Insert)N +3401(cold)X +3787(7.5)X +4078(10.3)X +3401 1529(warm)N +3787(6.7)X +4078(10.9)X +10 f +2618 1539(i)N +2629(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii)X +2618(c)X +1479(c)Y +1399(c)Y +1319(c)Y +1239(c)Y +1159(c)Y +1079(c)Y +999(c)Y +919(c)Y +839(c)Y +759(c)Y +3341 1539(c)N +1479(c)Y +1399(c)Y +1319(c)Y +1239(c)Y +1159(c)Y +1079(c)Y +999(c)Y +919(c)Y +839(c)Y +759(c)Y +3666 1539(c)N +1479(c)Y +1399(c)Y +1319(c)Y +1239(c)Y +1159(c)Y +1079(c)Y +999(c)Y +919(c)Y +839(c)Y +759(c)Y +3968 1539(c)N +1479(c)Y +1399(c)Y +1319(c)Y +1239(c)Y +1159(c)Y +1079(c)Y +999(c)Y +919(c)Y +839(c)Y +759(c)Y +4309 1539(c)N +1479(c)Y +1399(c)Y +1319(c)Y +1239(c)Y +1159(c)Y +1079(c)Y +999(c)Y +919(c)Y +839(c)Y +759(c)Y +3 f +587 1785(Table)N +823(2:)X +931(Local)X +1163(MC680x0)X +1538(Performance)X +2026(of)X +2133(Several)X +587 1875(Systems)N +883(on)X +987(OO1.)X +2667 1785(Table)N +2909(3:)X +3023(Local)X +3260(vs.)X +3397(Remote)X +3707(Performance)X +4200(of)X +2667 1875(LIBTP)N +2926(on)X +3030(OO1.)X +1 f +10 f +555 1998(h)N +579(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)X +1 f +555 2274(two)N +696(disk)X +850(pages,)X +1074(but)X +1197(the)X +1316(links)X +1492(require)X +1741(only)X +1904(one,)X +2061(regardless)X +2408(of)X +2496(database)X +2794(size.)X +2980(Cattell)X +3214(reports)X +3458(that)X +3599(lookups)X +3873(using)X +4067(B-trees)X +555 2364(instead)N +808(of)X +901(links)X +1082(makes)X +1313(traversal)X +1616(take)X +1776(twice)X +1976(as)X +2069(long)X +2237(in)X +2325(INDEX.)X +2641(Adding)X +2907(a)X +2969(link)X +3119(access)X +3351(method)X +3617(to)X +3 f +3704(db)X +1 f +3792(\(3\))X +3911(or)X +4003(using)X +4201(the)X +555 2454(existing)N +828(hash)X +995(method)X +1255(would)X +1475(apparently)X +1834(be)X +1930(a)X +1986(good)X +2166(idea.)X +755 2577(Both)N +936(OODBMS)X +1304(and)X +1446(INDEX)X +1722(issue)X +1908 0.1944(coarser-granularity)AX +2545(locks)X +2739(than)X +2902(LIBTP.)X +3189(This)X +3356(limits)X +3562(concurrency)X +3985(for)X +4104(multi-)X +555 2667(user)N +711(applications,)X +1140(but)X +1264(helps)X +1455(single-user)X +1829(applications.)X +2278(In)X +2367(addition,)X +2671(the)X +2791(fact)X +2934(that)X +3076(LIBTP)X +3319(releases)X +3595(B-tree)X +3817(locks)X +4007(early)X +4189(is)X +4263(a)X +555 2757(drawback)N +896(in)X +986(OO1.)X +1210(Since)X +1416(there)X +1605(is)X +1686(no)X +1793(concurrency)X +2218(in)X +2307(the)X +2432(benchmark,)X +2836(high-concurrency)X +3430(strategies)X +3760(only)X +3929(show)X +4125(up)X +4232(as)X +555 2847(increased)N +882(locking)X +1145(overhead.)X +1503(Finally,)X +1772(the)X +1892(architecture)X +2294(of)X +2383(the)X +2503(LIBTP)X +2747(implementation)X +3271(was)X +3418(substantially)X +3844(different)X +4143(from)X +555 2937(that)N +702(of)X +796(either)X +1006(OODBMS)X +1375(or)X +1469(INDEX.)X +1786(Both)X +1968(of)X +2062(those)X +2258(systems)X +2538(do)X +2645(the)X +2770(searches)X +3070(in)X +3159(the)X +3284(user's)X +3503(address)X +3771(space,)X +3997(and)X +4139(issue)X +555 3027(requests)N +844(for)X +964(pages)X +1173(to)X +1260(the)X +1383(server)X +1605(process.)X +1911(Pages)X +2123(are)X +2247(cached)X +2496(in)X +2583(the)X +2706(client,)X +2929(and)X +3070(many)X +3273(queries)X +3530(can)X +3667(be)X +3768(satis\256ed)X +4055(without)X +555 3117(contacting)N +910(the)X +1029(server)X +1247(at)X +1326(all.)X +1467(LIBTP)X +1710(submits)X +1979(all)X +2080(the)X +2199(queries)X +2452(to)X +2535(the)X +2653(server)X +2870(process,)X +3151(and)X +3287(receives)X +3571(database)X +3868(records)X +4125(back;)X +555 3207(it)N +619(does)X +786(no)X +886(client)X +1084(caching.)X +755 3330(The)N +911(RDBMS)X +1221(architecture)X +1632(is)X +1716(much)X +1925(closer)X +2148(to)X +2241(that)X +2392(of)X +2490(LIBTP.)X +2783(A)X +2872(server)X +3100(process)X +3372(receives)X +3667(queries)X +3930(and)X +4076(returns)X +555 3420(results)N +786(to)X +870(a)X +928(client.)X +1168(The)X +1315(timing)X +1545(results)X +1776(in)X +1860(table)X +2038(two)X +2180(clearly)X +2421(show)X +2612(that)X +2754(the)X +2874(conventional)X +3309(database)X +3607(client/server)X +4025(model)X +4246(is)X +555 3510(expensive.)N +941(LIBTP)X +1188(outperforms)X +1605(the)X +1728(RDBMS)X +2032(on)X +2136(traversal)X +2437(and)X +2577(insertion.)X +2921(We)X +3057(speculate)X +3380(that)X +3524(this)X +3663(is)X +3740(due)X +3880(in)X +3966(part)X +4115(to)X +4201(the)X +555 3600(overhead)N +870(of)X +957(query)X +1160(parsing,)X +1436(optimization,)X +1880(and)X +2016(repeated)X +2309(interpretation)X +2761(of)X +2848(the)X +2966(plan)X +3124(tree)X +3265(in)X +3347(the)X +3465(RDBMS')X +3791(query)X +3994(executor.)X +755 3723(Table)N +962(three)X +1147(shows)X +1371(the)X +1492(differences)X +1873(between)X +2164(local)X +2343(and)X +2482(remote)X +2728(execution)X +3063(of)X +3153(LIBTP's)X +3456(OO1)X +3635(implementation)X +4160(on)X +4263(a)X +555 3813(DECstation.)N +989(We)X +1122(measured)X +1451(performance)X +1879(with)X +2042(a)X +2099(populated)X +2436(\(warm\))X +2694(cache)X +2899(and)X +3036(an)X +3133(empty)X +3354(\(cold\))X +3567(cache.)X +3812(Reported)X +4126(times)X +555 3903(are)N +681(the)X +806(means)X +1037(of)X +1130(twenty)X +1374(tests,)X +1562(and)X +1704(are)X +1829(in)X +1917(seconds.)X +2237(Standard)X +2548(deviations)X +2903(were)X +3086(within)X +3316(seven)X +3525(percent)X +3788(of)X +3881(the)X +4005(mean)X +4205(for)X +555 3993(remote,)N +818(and)X +954(two)X +1094(percent)X +1351(of)X +1438(the)X +1556(mean)X +1750(for)X +1864(local.)X +755 4116(The)N +914(20ms)X +1121(overhead)X +1450(of)X +1551(TCP/IP)X +1824(on)X +1938(an)X +2048(Ethernet)X +2354(entirely)X +2633(accounts)X +2948(for)X +3076(the)X +3207(difference)X +3567(in)X +3662(speed.)X +3918(The)X +4076(remote)X +555 4206(traversal)N +857(times)X +1055(are)X +1179(nearly)X +1405(double)X +1648(the)X +1771(local)X +1952(times)X +2150(because)X +2430(we)X +2549(do)X +2653(index)X +2855(lookups)X +3132(and)X +3272(part)X +3421(fetches)X +3673(in)X +3759(separate)X +4047(queries.)X +555 4296(It)N +629(would)X +854(make)X +1053(sense)X +1252(to)X +1339(do)X +1444(indexed)X +1723(searches)X +2021(on)X +2126(the)X +2248(server,)X +2489(but)X +2615(we)X +2733(were)X +2914(unwilling)X +3244(to)X +3330(hard-code)X +3676(knowledge)X +4052(of)X +4143(OO1)X +555 4386(indices)N +803(into)X +948(our)X +1075(LIBTP)X +1317(TCL)X +1488(server.)X +1745(Cold)X +1920(and)X +2056(warm)X +2259(insertion)X +2559(times)X +2752(are)X +2871(identical)X +3167(since)X +3352(insertions)X +3683(do)X +3783(not)X +3905(bene\256t)X +4143(from)X +555 4476(caching.)N +755 4599(One)N +915(interesting)X +1279(difference)X +1632(shown)X +1867(by)X +1973(table)X +2155(three)X +2342(is)X +2421(the)X +2545(cost)X +2700(of)X +2793(forward)X +3074(versus)X +3305(backward)X +3644(traversal.)X +3987(When)X +4205(we)X +555 4689(built)N +725(the)X +847(database,)X +1168(we)X +1285(inserted)X +1562(parts)X +1741(in)X +1826(part)X +2 f +1974(id)X +1 f +2059(order.)X +2292(We)X +2427(built)X +2596(the)X +2717(indices)X +2967(at)X +3048(the)X +3169(same)X +3357(time.)X +3562(Therefore,)X +3923(the)X +4044(forward)X +555 4779(index)N +757(had)X +897(keys)X +1068(inserted)X +1346(in)X +1432(order,)X +1646(while)X +1848(the)X +1970(backward)X +2307(index)X +2509(had)X +2649(keys)X +2820(inserted)X +3098(more)X +3286(randomly.)X +3656(In-order)X +3943(insertion)X +4246(is)X +555 4885(pessimal)N +858(for)X +975(B-tree)X +1199(indices,)X +1469(so)X +1563(the)X +1684(forward)X +1962(index)X +2163(is)X +2239(much)X +2440(larger)X +2651(than)X +2812(the)X +2933(backward)X +3269(one)X +7 s +3385 4853(5)N +10 s +4885(.)Y +3476(This)X +3640(larger)X +3850(size)X +3997(shows)X +4219(up)X +555 4975(as)N +642(extra)X +823(disk)X +976(reads)X +1166(in)X +1248(the)X +1366(cold)X +1524(benchmark.)X +3 f +555 5161(6.)N +655(Conclusions)X +1 f +755 5284(LIBTP)N +1006(provides)X +1311(the)X +1438(basic)X +1632(building)X +1927(blocks)X +2165(to)X +2256(support)X +2525(transaction)X +2906(protection.)X +3300(In)X +3396(comparison)X +3799(with)X +3970(traditional)X +555 5374(Unix)N +746(libraries)X +1040(and)X +1187(commercial)X +1597(systems,)X +1900(it)X +1974(offers)X +2192(a)X +2258(variety)X +2511(of)X +2608(tradeoffs.)X +2964(Using)X +3185(complete)X +3509(transaction)X +3891(protection)X +4246(is)X +555 5464(more)N +747(complicated)X +1166(than)X +1331(simply)X +1575(adding)X +3 f +1820(fsync)X +1 f +1998(\(2\))X +2119(and)X +3 f +2262(\257ock)X +1 f +2426(\(2\))X +2547(calls)X +2721(to)X +2810(code,)X +3008(but)X +3136(it)X +3206(is)X +3285(faster)X +3490(in)X +3578(some)X +3773(cases)X +3969(and)X +4111(offers)X +8 s +10 f +555 5536(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh)N +5 s +1 f +727 5614(5)N +8 s +763 5639(The)N +878(next)X +1004(release)X +1196(of)X +1265(the)X +1359(4.4BSD)X +1580(access)X +1758(method)X +1966(will)X +2082(automatically)X +2446(detect)X +2614(and)X +2722(compensate)X +3039(for)X +3129(in-order)X +3350(insertion,)X +3606(eliminating)X +3914(this)X +4023(problem.)X + +16 p +%%Page: 16 16 +8 s 8 xH 0 xS 1 f +10 s +3 f +1 f +555 630(stricter)N +801(guarantees)X +1168(\(atomicity,)X +1540(consistency,)X +1957(isolation,)X +2275(and)X +2414(durability\).)X +2815(If)X +2892(the)X +3013(data)X +3170(to)X +3255(be)X +3354(protected)X +3676(are)X +3798(already)X +4058(format-)X +555 720(ted)N +675(\()X +2 f +702(i.e.)X +1 f +821(use)X +949(one)X +1086(of)X +1174(the)X +1293(database)X +1591(access)X +1818(methods\),)X +2157(then)X +2316(adding)X +2555(transaction)X +2928(protection)X +3274(requires)X +3554(no)X +3655(additional)X +3996(complex-)X +555 810(ity,)N +679(but)X +801(incurs)X +1017(a)X +1073(performance)X +1500(penalty)X +1756(of)X +1843(approximately)X +2326(15%.)X +755 933(In)N +844(comparison)X +1240(with)X +1404(commercial)X +1805(database)X +2104(systems,)X +2399(the)X +2519(tradeoffs)X +2827(are)X +2948(more)X +3135(complex.)X +3473(LIBTP)X +3717(does)X +3886(not)X +4009(currently)X +555 1023(support)N +825(a)X +891(standard)X +1193(query)X +1406(language.)X +1766(The)X +1921(TCL-based)X +2312(server)X +2539(process)X +2810(allows)X +3049(a)X +3115(certain)X +3364(ease)X +3533(of)X +3630(use)X +3767(which)X +3993(would)X +4223(be)X +555 1113(enhanced)N +882(with)X +1047(a)X +1106(more)X +1294(user-friendly)X +1732(interface)X +2037(\()X +2 f +2064(e.g.)X +1 f +2203(a)X +2261(windows)X +2572(based)X +2777(query-by-form)X +3272(application\),)X +3697(for)X +3813(which)X +4031(we)X +4147(have)X +555 1203(a)N +620(working)X +916(prototype.)X +1292(When)X +1513(accesses)X +1815(do)X +1924(not)X +2055(require)X +2312(sophisticated)X +2758(query)X +2969(processing,)X +3360(the)X +3486(TCL)X +3665(interface)X +3975(is)X +4056(an)X +4160(ade-)X +555 1293(quate)N +756(solution.)X +1080(What)X +1281(LIBTP)X +1529(fails)X +1693(to)X +1781(provide)X +2052(in)X +2140(functionality,)X +2595(it)X +2665(makes)X +2896(up)X +3002(for)X +3122(in)X +3210(performance)X +3643(and)X +3785(\257exibility.)X +4161(Any)X +555 1383(application)N +931(may)X +1089(make)X +1283(use)X +1410(of)X +1497(its)X +1592(record)X +1818(interface)X +2120(or)X +2207(the)X +2325(more)X +2510(primitive)X +2823(log,)X +2965(lock,)X +3143(and)X +3279(buffer)X +3496(calls.)X +755 1506(Future)N +987(work)X +1175(will)X +1322(focus)X +1519(on)X +1621(overcoming)X +2026(some)X +2217(of)X +2306(the)X +2426(areas)X +2614(in)X +2698(which)X +2916(LIBTP)X +3160(is)X +3235(currently)X +3547(de\256cient)X +3845(and)X +3983(extending)X +555 1596(its)N +652(transaction)X +1026(model.)X +1288(The)X +1435(addition)X +1719(of)X +1808(an)X +1905(SQL)X +2077(parser)X +2295(and)X +2432(forms)X +2640(front)X +2817(end)X +2954(will)X +3099(improve)X +3387(the)X +3506(system's)X +3807(ease)X +3967(of)X +4055(use)X +4183(and)X +555 1686(make)N +750(it)X +815(more)X +1001(competitive)X +1400(with)X +1563(commercial)X +1963(systems.)X +2277(In)X +2365(the)X +2484(long)X +2647(term,)X +2835(we)X +2950(would)X +3170(like)X +3310(to)X +3392(add)X +3528(generalized)X +3919(hierarchical)X +555 1776(locking,)N +836(nested)X +1062(transactions,)X +1486(parallel)X +1748(transactions,)X +2171(passing)X +2431(of)X +2518(transactions)X +2921(between)X +3209(processes,)X +3557(and)X +3693(distributed)X +4055(commit)X +555 1866(handling.)N +900(In)X +992(the)X +1115(short)X +1300(term,)X +1492(the)X +1614(next)X +1776(step)X +1929(is)X +2006(to)X +2092(integrate)X +2397(LIBTP)X +2643(with)X +2809(the)X +2931(most)X +3110(recent)X +3331(release)X +3579(of)X +3670(the)X +3792(database)X +4093(access)X +555 1956(routines)N +833(and)X +969(make)X +1163(it)X +1227(freely)X +1435(available)X +1745(via)X +1863(anonymous)X +2252(ftp.)X +3 f +555 2142(7.)N +655(Acknowledgements)X +1 f +755 2265(We)N +888(would)X +1109(like)X +1250(to)X +1332(thank)X +1530(John)X +1701(Wilkes)X +1948(and)X +2084(Carl)X +2242(Staelin)X +2484(of)X +2571(Hewlett-Packard)X +3131(Laboratories)X +3557(and)X +3693(Jon)X +3824(Krueger.)X +4148(John)X +555 2355(and)N +694(Carl)X +855(provided)X +1162(us)X +1255(with)X +1419(an)X +1517(extra)X +1700(disk)X +1855(for)X +1971(the)X +2091(HP)X +2215(testbed)X +2464(less)X +2606(than)X +2766(24)X +2868(hours)X +3068(after)X +3238(we)X +3354(requested)X +3684(it.)X +3770(Jon)X +3903(spent)X +4094(count-)X +555 2445(less)N +699(hours)X +901(helping)X +1164(us)X +1258(understand)X +1633(the)X +1754(intricacies)X +2107(of)X +2197(commercial)X +2599(database)X +2899(products)X +3198(and)X +3337(their)X +3507(behavior)X +3811(under)X +4017(a)X +4076(variety)X +555 2535(of)N +642(system)X +884(con\256gurations.)X +3 f +555 2721(8.)N +655(References)X +1 f +555 2901([ANDR89])N +942(Andrade,)X +1265(J.,)X +1361(Carges,)X +1629(M.,)X +1765(Kovach,)X +2060(K.,)X +2183(``Building)X +2541(an)X +2642(On-Line)X +2939(Transaction)X +3343(Processing)X +3715(System)X +3975(On)X +4098(UNIX)X +727 2991(System)N +982(V'',)X +2 f +1134(CommUNIXations)X +1 f +1725(,)X +1765 0.2188(November/December)AX +2477(1989.)X +555 3171([BAY77])N +878(Bayer,)X +1110(R.,)X +1223(Schkolnick,)X +1623(M.,)X +1754(``Concurrency)X +2243(of)X +2330(Operations)X +2702(on)X +2802(B-Trees'',)X +2 f +3155(Acta)X +3322(Informatica)X +1 f +3700(,)X +3740(1977.)X +555 3351([BERN80])N +936(Bernstein,)X +1297(P.,)X +1415(Goodman,)X +1785(N.,)X +1917(``Timestamp)X +2365(Based)X +2595(Algorithms)X +2992(for)X +3119(Concurrency)X +3567(Control)X +3844(in)X +3939(Distributed)X +727 3441(Database)N +1042(Systems'',)X +2 f +1402(Proceedings)X +1823(6th)X +1945(International)X +2387(Conference)X +2777(on)X +2877(Very)X +3049(Large)X +3260(Data)X +3440(Bases)X +1 f +3627(,)X +3667(October)X +3946(1980.)X +555 3621([BSD91])N +864(DB\(3\),)X +2 f +1109(4.4BSD)X +1376(Unix)X +1552(Programmer's)X +2044(Manual)X +2313(Reference)X +2655(Guide)X +1 f +2851(,)X +2891(University)X +3249(of)X +3336(California,)X +3701(Berkeley,)X +4031(1991.)X +555 3801([CATT91])N +923(Cattell,)X +1181(R.G.G.,)X +1455(``An)X +1632(Engineering)X +2049(Database)X +2369(Benchmark'',)X +2 f +2838(The)X +2983(Benchmark)X +3373(Handbook)X +3731(for)X +3848(Database)X +4179(and)X +727 3891(Transaction)N +1133(Processing)X +1509(Systems)X +1 f +1763(,)X +1803(J.)X +1874(Gray,)X +2075(editor,)X +2302(Morgan)X +2576(Kaufman)X +2895(1991.)X +555 4071([CHEN91])N +929(Cheng,)X +1180(E.,)X +1291(Chang,)X +1542(E.,)X +1653(Klein,)X +1872(J.,)X +1964(Lee,)X +2126(D.,)X +2245(Lu,)X +2375(E.,)X +2485(Lutgardo,)X +2820(A.,)X +2939(Obermarck,)X +3342(R.,)X +3456(``An)X +3629(Open)X +3824(and)X +3961(Extensible)X +727 4161(Event-Based)N +1157(Transaction)X +1556(Manager'',)X +2 f +1936(Proceedings)X +2357(1991)X +2537(Summer)X +2820(Usenix)X +1 f +3043(,)X +3083(Nashville,)X +3430(TN,)X +3577(June)X +3744(1991.)X +555 4341([CHOU85])N +943(Chou,)X +1163(H.,)X +1288(DeWitt,)X +1570(D.,)X +1694(``An)X +1872(Evaluation)X +2245(of)X +2338(Buffer)X +2574(Management)X +3019(Strategies)X +3361(for)X +3481(Relational)X +3836(Database)X +4157(Sys-)X +727 4431(tems'',)N +2 f +972(Proceedings)X +1393(of)X +1475(the)X +1593(11th)X +1755(International)X +2197(Conference)X +2587(on)X +2687(Very)X +2859(Large)X +3070(Databases)X +1 f +3408(,)X +3448(1985.)X +555 4611([DEWI84])N +925(DeWitt,)X +1207(D.,)X +1331(Katz,)X +1529(R.,)X +1648(Olken,)X +1890(F.,)X +2000(Shapiro,)X +2295(L.,)X +2410(Stonebraker,)X +2843(M.,)X +2979(Wood,)X +3220(D.,)X +3343(``Implementation)X +3929(Techniques)X +727 4701(for)N +841(Main)X +1030(Memory)X +1326(Database)X +1641(Systems'',)X +2 f +2001(Proceedings)X +2422(of)X +2504(SIGMOD)X +1 f +2812(,)X +2852(pp.)X +2972(1-8,)X +3119(June)X +3286(1984.)X +555 4881([GRAY76])N +944(Gray,)X +1153(J.,)X +1252(Lorie,)X +1474(R.,)X +1595(Putzolu,)X +1887(F.,)X +1999(and)X +2143(Traiger,)X +2428(I.,)X +2522(``Granularity)X +2973(of)X +3067(locks)X +3263(and)X +3406(degrees)X +3679(of)X +3773(consistency)X +4174(in)X +4263(a)X +727 4971(large)N +909(shared)X +1140(data)X +1295(base'',)X +2 f +1533(Modeling)X +1861(in)X +1944(Data)X +2125(Base)X +2301(Management)X +2740(Systems)X +1 f +2994(,)X +3034(Elsevier)X +3317(North)X +3524(Holland,)X +3822(New)X +3994(York,)X +4199(pp.)X +727 5061(365-394.)N +555 5241([HAER83])N +931(Haerder,)X +1235(T.)X +1348(Reuter,)X +1606(A.)X +1728(``Principles)X +2126(of)X +2217(Transaction-Oriented)X +2928(Database)X +3246(Recovery'',)X +2 f +3651(Computing)X +4029(Surveys)X +1 f +4279(,)X +727 5331(15\(4\);)N +943(237-318,)X +1250(1983.)X +555 5511([KUNG81])N +943(Kung,)X +1162(H.)X +1261(T.,)X +1371(Richardson,)X +1777(J.,)X +1869(``On)X +2042(Optimistic)X +2400(Methods)X +2701(for)X +2816(Concurrency)X +3252(Control'',)X +2 f +3591(ACM)X +3781(Transactions)X +4219(on)X +727 5601(Database)N +1054(Systems)X +1 f +1328(6\(2\);)X +1504(213-226,)X +1811(1981.)X + +17 p +%%Page: 17 17 +10 s 10 xH 0 xS 1 f +3 f +1 f +555 630([LEHM81])N +939(Lehman,)X +1245(P.,)X +1352(Yao,)X +1529(S.,)X +1636(``Ef\256cient)X +1989(Locking)X +2279(for)X +2396(Concurrent)X +2780(Operations)X +3155(on)X +3258(B-trees'',)X +2 f +3587(ACM)X +3779(Transactions)X +4219(on)X +727 720(Database)N +1054(Systems)X +1 f +1308(,)X +1348(6\(4\),)X +1522(December)X +1873(1981.)X +555 900([MOHA91])N +964(Mohan,)X +1241(C.,)X +1364(Pirahesh,)X +1690(H.,)X +1818(``ARIES-RRH:)X +2366(Restricted)X +2721(Repeating)X +3076(of)X +3173(History)X +3442(in)X +3533(the)X +3660(ARIES)X +3920(Transaction)X +727 990(Recovery)N +1055(Method'',)X +2 f +1398(Proceedings)X +1819(7th)X +1941(International)X +2383(Conference)X +2773(on)X +2873(Data)X +3053(Engineering)X +1 f +3449(,)X +3489(Kobe,)X +3703(Japan,)X +3926(April)X +4115(1991.)X +555 1170([NODI90])N +914(Nodine,)X +1194(M.,)X +1328(Zdonik,)X +1602(S.,)X +1709(``Cooperative)X +2178(Transaction)X +2580(Hierarchies:)X +2996(A)X +3077(Transaction)X +3479(Model)X +3711(to)X +3796(Support)X +4072(Design)X +727 1260(Applications'',)N +2 f +1242(Proceedings)X +1675(16th)X +1849(International)X +2303(Conference)X +2704(on)X +2815(Very)X +2998(Large)X +3220(Data)X +3411(Bases)X +1 f +3598(,)X +3649(Brisbane,)X +3985(Australia,)X +727 1350(August)N +978(1990.)X +555 1530([OUST90])N +923(Ousterhout,)X +1324(J.,)X +1420(``Tcl:)X +1648(An)X +1771(Embeddable)X +2197(Command)X +2555(Language'',)X +2 f +2971(Proceedings)X +3396(1990)X +3580(Winter)X +3822(Usenix)X +1 f +4045(,)X +4089(Wash-)X +727 1620(ington,)N +971(D.C.,)X +1162(January)X +1432(1990.)X +555 1800([POSIX91])N +955(``Unapproved)X +1441(Draft)X +1645(for)X +1773(Realtime)X +2096(Extension)X +2450(for)X +2578(Portable)X +2879(Operating)X +3234(Systems'',)X +3608(Draft)X +3812(11,)X +3946(October)X +4239(7,)X +727 1890(1991,)N +927(IEEE)X +1121(Computer)X +1461(Society.)X +555 2070([ROSE91])N +925(Rosenblum,)X +1341(M.,)X +1484(Ousterhout,)X +1892(J.,)X +1995(``The)X +2206(Design)X +2464(and)X +2611(Implementation)X +3149(of)X +3247(a)X +3314(Log-Structured)X +3835(File)X +3990(System'',)X +2 f +727 2160(Proceedings)N +1148(of)X +1230(the)X +1348(13th)X +1510(Symposium)X +1895(on)X +1995(Operating)X +2344(Systems)X +2618(Principles)X +1 f +2947(,)X +2987(1991.)X +555 2340([SELT91])N +904(Seltzer,)X +1171(M.,)X +1306(Stonebraker,)X +1738(M.,)X +1873(``Read)X +2116(Optimized)X +2478(File)X +2626(Systems:)X +2938(A)X +3020(Performance)X +3454(Evaluation'',)X +2 f +3898(Proceedings)X +727 2430(7th)N +849(Annual)X +1100(International)X +1542(Conference)X +1932(on)X +2032(Data)X +2212(Engineering)X +1 f +2608(,)X +2648(Kobe,)X +2862(Japan,)X +3085(April)X +3274(1991.)X +555 2610([SPEC88])N +907(Spector,)X +1200(Rausch,)X +1484(Bruell,)X +1732(``Camelot:)X +2107(A)X +2192(Flexible,)X +2501(Distributed)X +2888(Transaction)X +3294(Processing)X +3668(System'',)X +2 f +4004(Proceed-)X +727 2700(ings)N +880(of)X +962(Spring)X +1195(COMPCON)X +1606(1988)X +1 f +(,)S +1806(February)X +2116(1988.)X +555 2880([SQL86])N +862(American)X +1201(National)X +1499(Standards)X +1836(Institute,)X +2139(``Database)X +2509(Language)X +2847(SQL'',)X +3093(ANSI)X +3301(X3.135-1986)X +3747(\(ISO)X +3924(9075\),)X +4152(May)X +727 2970(1986.)N +555 3150([STON81])N +919(Stonebraker,)X +1348(M.,)X +1480(``Operating)X +1876(System)X +2132(Support)X +2406(for)X +2520(Database)X +2835(Management'',)X +2 f +3348(Communications)X +3910(of)X +3992(the)X +4110(ACM)X +1 f +4279(,)X +727 3240(1981.)N +555 3420([SULL92])N +925(Sullivan,)X +1247(M.,)X +1394(Olson,)X +1641(M.,)X +1788(``An)X +1976(Index)X +2195(Implementation)X +2737(Supporting)X +3127(Fast)X +3295(Recovery)X +3638(for)X +3767(the)X +3900(POSTGRES)X +727 3510(Storage)N +1014(System'',)X +1365(to)X +1469(appear)X +1726(in)X +2 f +1830(Proceedings)X +2272(8th)X +2415(Annual)X +2687(International)X +3150(Conference)X +3561(on)X +3682(Data)X +3883(Engineering)X +1 f +4279(,)X +727 3600(Tempe,)N +990(Arizona,)X +1289(February)X +1599(1992.)X +555 3780([TPCB90])N +914(Transaction)X +1319(Processing)X +1692(Performance)X +2129(Council,)X +2428(``TPC)X +2653(Benchmark)X +3048(B'',)X +3200(Standard)X +3510(Speci\256cation,)X +3973(Waterside)X +727 3870(Associates,)N +1110(Fremont,)X +1421(CA.,)X +1592(1990.)X +555 4050([YOUN91])N +947(Young,)X +1211(M.)X +1328(W.,)X +1470(Thompson,)X +1858(D.)X +1962(S.,)X +2072(Jaffe,)X +2274(E.,)X +2388(``A)X +2525(Modular)X +2826(Architecture)X +3253(for)X +3372(Distributed)X +3757(Transaction)X +4161(Pro-)X +727 4140(cessing'',)N +2 f +1057(Proceedings)X +1478(1991)X +1658(Winter)X +1896(Usenix)X +1 f +2119(,)X +2159(Dallas,)X +2404(TX,)X +2551(January)X +2821(1991.)X +3 f +755 4263(Margo)N +1008(I.)X +1080(Seltzer)X +1 f +1338(is)X +1411(a)X +1467(Ph.D.)X +1669(student)X +1920(in)X +2002(the)X +2120(Department)X +2519(of)X +2606(Electrical)X +2934(Engineering)X +3346(and)X +3482(Computer)X +3822(Sciences)X +4123(at)X +4201(the)X +555 4353(University)N +919(of)X +1012(California,)X +1383(Berkeley.)X +1739(Her)X +1886(research)X +2181(interests)X +2474(include)X +2735(\256le)X +2862(systems,)X +3160(databases,)X +3513(and)X +3654(transaction)X +4031(process-)X +555 4443(ing)N +686(systems.)X +1008(She)X +1157(spent)X +1355(several)X +1612(years)X +1811(working)X +2107(at)X +2194(startup)X +2441(companies)X +2813(designing)X +3153(and)X +3298(implementing)X +3771(\256le)X +3902(systems)X +4183(and)X +555 4533(transaction)N +929(processing)X +1294(software)X +1592(and)X +1729(designing)X +2061(microprocessors.)X +2648(Ms.)X +2791(Seltzer)X +3035(received)X +3329(her)X +3453(AB)X +3585(in)X +3668(Applied)X +3947(Mathemat-)X +555 4623(ics)N +664(from)X +840 0.1953(Harvard/Radcliffe)AX +1445(College)X +1714(in)X +1796(1983.)X +755 4746(In)N +845(her)X +971(spare)X +1163(time,)X +1347(Margo)X +1583(can)X +1717(usually)X +1970(be)X +2068(found)X +2277(preparing)X +2607(massive)X +2887(quantities)X +3220(of)X +3309(food)X +3478(for)X +3594(hungry)X +3843(hordes,)X +4099(study-)X +555 4836(ing)N +677(Japanese,)X +1003(or)X +1090(playing)X +1350(soccer)X +1576(with)X +1738(an)X +1834(exciting)X +2112(Bay)X +2261(Area)X +2438(Women's)X +2770(Soccer)X +3009(team,)X +3205(the)X +3323(Berkeley)X +3633(Bruisers.)X +3 f +755 5049(Michael)N +1056(A.)X +1159(Olson)X +1 f +1383(is)X +1461(a)X +1522(Master's)X +1828(student)X +2084(in)X +2170(the)X +2292(Department)X +2695(of)X +2786(Electrical)X +3118(Engineering)X +3534(and)X +3674(Computer)X +4018(Sciences)X +555 5139(at)N +645(the)X +774(University)X +1143(of)X +1241(California,)X +1617(Berkeley.)X +1978(His)X +2120(primary)X +2405(interests)X +2703(are)X +2833(database)X +3141(systems)X +3425(and)X +3572(mass)X +3763(storage)X +4026(systems.)X +555 5229(Mike)N +759(spent)X +963(two)X +1118(years)X +1323(working)X +1625(for)X +1754(a)X +1825(commercial)X +2239(database)X +2551(system)X +2808(vendor)X +3066(before)X +3307(joining)X +3567(the)X +3699(Postgres)X +4004(Research)X +555 5319(Group)N +780(at)X +858(Berkeley)X +1168(in)X +1250(1988.)X +1470(He)X +1584(received)X +1877(his)X +1990(B.A.)X +2161(in)X +2243(Computer)X +2583(Science)X +2853(from)X +3029(Berkeley)X +3339(in)X +3421(May)X +3588(1991.)X +755 5442(Mike)N +945(only)X +1108(recently)X +1388(transferred)X +1758(into)X +1903(Sin)X +2030(City,)X +2208(but)X +2330(is)X +2403(rapidly)X +2650(adopting)X +2950(local)X +3126(customs)X +3408(and)X +3544(coloration.)X +3929(In)X +4016(his)X +4129(spare)X +555 5532(time,)N +742(he)X +843(organizes)X +1176(informal)X +1477(Friday)X +1711(afternoon)X +2043(study)X +2240(groups)X +2482(to)X +2568(discuss)X +2823(recent)X +3044(technical)X +3358(and)X +3498(economic)X +3834(developments.)X +555 5622(Among)N +815(his)X +928(hobbies)X +1197(are)X +1316(Charles)X +1581(Dickens,)X +1884(Red)X +2033(Rock,)X +2242(and)X +2378(speaking)X +2683(Dutch)X +2899(to)X +2981(anyone)X +3233(who)X +3391(will)X +3535(permit)X +3764(it.)X + +17 p +%%Trailer +xt + +xs + diff --git a/src/util/db2/docs/mpool.3.ps b/src/util/db2/docs/mpool.3.ps new file mode 100644 index 000000000..816c9243c --- /dev/null +++ b/src/util/db2/docs/mpool.3.ps @@ -0,0 +1,320 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 129.01(MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 129.01(anual MPOOL\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 84 S +(ME).18 E F0(mpool \255 shared memory b)108 96 Q(uf)-.2 E(fer pool)-.25 E F1 +(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(#include )-.4 E +(#include )108 136.8 Q(MPOOL *)108 160.8 Q(mpool_open \(DBT *k)108 +172.8 Q(ey)-.1 E 2.5(,i)-.55 G(nt fd, pgno_t pagesize, pgno_t maxcache\);) +216.25 172.8 Q -.1(vo)108 196.8 S(id).1 E(mpool_\214lter \(MPOOL *mp, v)108 +208.8 Q(oid \(*pgin\)\(v)-.1 E(oid *, pgno_t, v)-.1 E(oid *\),)-.1 E -.1(vo)158 +220.8 S(id \(*pgout\)\(v).1 E(oid *, pgno_t, v)-.1 E(oid *\), v)-.1 E +(oid *pgcookie\);)-.1 E -.1(vo)108 244.8 S(id *).1 E +(mpool_new \(MPOOL *mp, pgno_t *pgnoaddr\);)108 256.8 Q -.1(vo)108 280.8 S +(id *).1 E(mpool_get \(MPOOL *mp, pgno_t pgno, u_int \215ags\);)108 292.8 Q +(int)108 316.8 Q(mpool_put \(MPOOL *mp, v)108 328.8 Q(oid *pgaddr)-.1 E 2.5(,u) +-.92 G(_int \215ags\);)290.62 328.8 Q(int)108 352.8 Q +(mpool_sync \(MPOOL *mp\);)108 364.8 Q(int)108 388.8 Q +(mpool_close \(MPOOL *mp\);)108 400.8 Q F1(DESCRIPTION)72 417.6 Q/F3 10 +/Times-Italic@0 SF(Mpool)108 429.6 Q F0 1.013(is the library interf)3.513 F +1.013(ace intended to pro)-.1 F 1.013(vide page oriented b)-.15 F(uf)-.2 E +1.012(fer management of \214les.)-.25 F 1.012(The b)6.012 F(uf)-.2 E(fers)-.25 +E(may be shared between processes.)108 441.6 Q .416(The function)108 458.4 R F3 +(mpool_open)2.916 E F0 .417(initializes a memory pool.)2.917 F(The)5.417 E F3 +-.1(ke)2.917 G(y)-.2 E F0(ar)2.917 E .417(gument is the byte string used to ne) +-.18 F(gotiate)-.15 E .697(between multiple processes wishing to share b)108 +470.4 R(uf)-.2 E 3.196(fers. If)-.25 F .696(the \214le b)3.196 F(uf)-.2 E .696 +(fers are mapped in shared memory)-.25 F 3.196(,a)-.65 G(ll)534.44 470.4 Q .894 +(processes using the same k)108 482.4 R 1.194 -.15(ey w)-.1 H .894 +(ill share the b).15 F(uf)-.2 E 3.394(fers. If)-.25 F F3 -.1(ke)3.394 G(y)-.2 E +F0 .895(is NULL, the b)3.395 F(uf)-.2 E .895(fers are mapped into pri)-.25 F +-.25(va)-.25 G(te).25 E(memory)108 494.4 Q 5.116(.T)-.65 G(he)154.406 494.4 Q +F3(fd)2.616 E F0(ar)2.616 E .115(gument is a \214le descriptor for the underly\ +ing \214le, which must be seekable.)-.18 F(If)5.115 E F3 -.1(ke)2.615 G(y)-.2 E +F0 .115(is non-)2.615 F(NULL and matches a \214le already being mapped, the)108 +506.4 Q F3(fd)2.5 E F0(ar)2.5 E(gument is ignored.)-.18 E(The)108 523.2 Q F3 +(pa)3.328 E -.1(ge)-.1 G(size).1 E F0(ar)3.329 E .829 +(gument is the size, in bytes, of the pages into which the \214le is brok)-.18 +F .829(en up.)-.1 F(The)5.829 E F3(maxcac)3.329 E(he)-.15 E F0(ar)108 535.2 Q +.153(gument is the maximum number of pages from the underlying \214le to cache\ + at an)-.18 F 2.653(yo)-.15 G .153(ne time.)451.308 535.2 R .153(This v)5.153 F +.153(alue is)-.25 F .099(not relati)108 547.2 R .399 -.15(ve t)-.25 H 2.599(ot) +.15 G .099(he number of processes which share a \214le')168.727 547.2 R 2.6(sb) +-.55 G(uf)350.39 547.2 Q .1(fers, b)-.25 F .1(ut will be the lar)-.2 F .1 +(gest v)-.18 F .1(alue speci\214ed by)-.25 F(an)108 559.2 Q 2.5(yo)-.15 G 2.5 +(ft)129.79 559.2 S(he processes sharing the \214le.)138.4 559.2 Q(The)108 576 Q +F3(mpool_\214lter)3.254 E F0 .754(function is intended to mak)3.254 F 3.254(et) +-.1 G .754(ransparent input and output processing of the pages possi-)301.778 +576 R 3.095(ble. If)108 588 R(the)3.095 E F3(pgin)3.095 E F0 .596 +(function is speci\214ed, it is called each time a b)3.095 F(uf)-.2 E .596 +(fer is read into the memory pool from the)-.25 F .125(backing \214le.)108 600 +R .125(If the)5.125 F F3(pgout)2.625 E F0 .125 +(function is speci\214ed, it is called each time a b)2.625 F(uf)-.2 E .125 +(fer is written into the backing \214le.)-.25 F .276 +(Both functions are are called with the)108 612 R F3(pgcookie)2.777 E F0 +(pointer)2.777 E 2.777(,t)-.4 G .277 +(he page number and a pointer to the page to being)337.27 612 R +(read or written.)108 624 Q .124(The function)108 640.8 R F3(mpool_ne)2.624 E +(w)-.15 E F0(tak)2.624 E .123(es an MPOOL pointer and an address as ar)-.1 F +2.623(guments. If)-.18 F 2.623(an)2.623 G .623 -.25(ew p)457.568 640.8 T .123 +(age can be allo-).25 F .944(cated, a pointer to the page is returned and the \ +page number is stored into the)108 652.8 R F3(pgnoaddr)3.445 E F0 3.445 +(address. Other)3.445 F(-)-.2 E(wise, NULL is returned and errno is set.)108 +664.8 Q 1.167(The function)108 681.6 R F3(mpool_g)3.667 E(et)-.1 E F0(tak)3.667 +E 1.167(es a MPOOL pointer and a page number as ar)-.1 F 3.666(guments. If)-.18 +F 1.166(the page e)3.666 F 1.166(xists, a)-.15 F .686 +(pointer to the page is returned.)108 693.6 R .687 +(Otherwise, NULL is returned and errno is set.)5.686 F .687 +(The \215ags parameter is not)5.687 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G +(istrib)132.57 732 Q 104.595(ution June)-.2 F(4, 1993)2.5 E(1)535 732 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 129.01(MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 129.01(anual MPOOL\(3\))340.17 48 R(currently used.)108 84 Q 1.463 +(The function)108 100.8 R/F1 10/Times-Italic@0 SF(mpool_put)3.963 E F0 1.462 +(unpins the page referenced by)3.962 F F1(pgaddr)3.962 E F0(.).73 E F1(Pgaddr) +6.462 E F0 1.462(must be an address pre)3.962 F(viously)-.25 E(returned by)108 +112.8 Q F1(mpool_g)2.5 E(et)-.1 E F0(or)2.5 E F1(mpool_ne)2.5 E(w)-.15 E F0 5 +(.T).31 G(he \215ag v)271.65 112.8 Q(alue is speci\214ed by)-.25 E F1(or)2.5 E +F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)434.74 112.8 S(he follo)443.35 112.8 Q +(wing v)-.25 E(alues:)-.25 E(MPOOL_DIR)108 129.6 Q(TY)-.6 E +(The page has been modi\214ed and needs to be written to the backing \214le.) +144 141.6 Q F1(Mpool_put)108 158.4 Q F0 +(returns 0 on success and -1 if an error occurs.)2.5 E .247(The function)108 +175.2 R F1(mpool_sync)2.747 E F0 .247(writes all modi\214ed pages associated w\ +ith the MPOOL pointer to the backing \214le.)2.747 F F1(Mpool_sync)108 187.2 Q +F0(returns 0 on success and -1 if an error occurs.)2.5 E(The)108 204 Q F1 +(mpool_close)2.698 E F0 .198(function free')2.698 F 2.698(su)-.55 G 2.698(pa) +245.432 204 S .498 -.15(ny a)257.57 204 T .198 +(llocated memory associated with the memory pool cookie.).15 F(Modi-)5.197 E +(\214ed pages are)108 216 Q/F2 10/Times-Bold@0 SF(not)2.5 E F0 +(written to the backing \214le.)2.5 E F1(Mpool_close)5 E F0 +(returns 0 on success and -1 if an error occurs.)2.5 E/F3 9/Times-Bold@0 SF +(ERR)72 232.8 Q(ORS)-.27 E F0(The)108 244.8 Q F1(mpool_open)2.938 E F0 .438 +(function may f)2.938 F .438(ail and set)-.1 F F1(errno)2.938 E F0 .438(for an) +2.938 F 2.938(yo)-.15 G 2.938(ft)344.87 244.8 S .439 +(he errors speci\214ed for the library routine)353.918 244.8 R F1(mal-)2.939 E +(loc)108 256.8 Q F0(\(3\).).31 E(The)108 273.6 Q F1(mpool_g)2.5 E(et)-.1 E F0 +(function may f)2.5 E(ail and set)-.1 E F1(errno)2.5 E F0(for the follo)2.5 E +(wing:)-.25 E([EINV)108 290.4 Q 29.98(AL] The)-1.35 F(requested record doesn') +2.5 E 2.5(te)-.18 G(xist.)305.96 290.4 Q(The)108 307.2 Q F1(mpool_ne)4.073 E(w) +-.15 E F0(and)4.073 E F1(mpool_g)4.073 E(et)-.1 E F0 1.573(functions may f) +4.073 F 1.573(ail and set)-.1 F F1(errno)4.073 E F0 1.573(for an)4.073 F 4.073 +(yo)-.15 G 4.073(ft)421.336 307.2 S 1.573(he errors speci\214ed for the)431.519 +307.2 R(library routines)108 319.2 Q F1 -.37(re)2.5 G(ad).37 E F0(\(2\)).77 E +F1 2.5(,w).54 G(rite)214.48 319.2 Q F0(\(2\)).18 E F1(,).54 E F0(and)2.5 E F1 +(malloc)2.5 E F0(\(3\).).31 E(The)108 336 Q F1(mpool_sync)4.287 E F0 1.787 +(function may f)4.287 F 1.787(ail and set)-.1 F F1(errno)4.288 E F0 1.788 +(for an)4.288 F 4.288(yo)-.15 G 4.288(ft)356.694 336 S 1.788 +(he errors speci\214ed for the library routine)367.092 336 R F1(write)108 348 Q +F0(\(2\).).18 E(The)108 364.8 Q F1(mpool_close)4.125 E F0 1.624(function may f) +4.125 F 1.624(ail and set)-.1 F F1(errno)4.124 E F0 1.624(for an)4.124 F 4.124 +(yo)-.15 G 4.124(ft)357.842 364.8 S 1.624 +(he errors speci\214ed for the library routine)368.076 364.8 R F1(fr)108 376.8 +Q(ee)-.37 E F0(\(3\).).18 E F3(SEE ALSO)72 393.6 Q F1(dbopen)108 405.6 Q F0 +(\(3\),).24 E F1(btr)2.5 E(ee)-.37 E F0(\(3\),).18 E F1(hash)2.5 E F0(\(3\),) +.28 E F1 -.37(re)2.5 G(cno).37 E F0(\(3\)).18 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5 +(yD)-.15 G(istrib)132.57 732 Q 104.595(ution June)-.2 F(4, 1993)2.5 E(2)535 732 +Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/docs/recno.3.ps b/src/util/db2/docs/recno.3.ps new file mode 100644 index 000000000..8ffccfca9 --- /dev/null +++ b/src/util/db2/docs/recno.3.ps @@ -0,0 +1,341 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 2 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 130.12(RECNO\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 130.12(anual RECNO\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 84 S +(ME).18 E F0(recno \255 record number database access method)108 96 Q F1 +(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q +(#include )-.4 E F1(DESCRIPTION)72 153.6 Q F0 1.158 +(The routine)108 165.6 R/F3 10/Times-Italic@0 SF(dbopen)3.658 E F0 1.158 +(is the library interf)3.658 F 1.158(ace to database \214les.)-.1 F 1.157 +(One of the supported \214le formats is record)6.158 F 1.159(number \214les.) +108 177.6 R 1.159(The general description of the database access methods is in) +6.159 F F3(dbopen)3.66 E F0 1.16(\(3\), this manual page).24 F +(describes only the recno speci\214c information.)108 189.6 Q 1.944 +(The record number data structure is either v)108 206.4 R 1.944 +(ariable or \214x)-.25 F 1.944 +(ed-length records stored in a \215at-\214le format,)-.15 F 2.04 +(accessed by the logical record number)108 218.4 R 7.04(.T)-.55 G 2.04(he e) +286.31 218.4 R 2.04(xistence of record number \214v)-.15 F 4.54(ei)-.15 G 2.04 +(mplies the e)442.1 218.4 R 2.04(xistence of)-.15 F .876 +(records one through four)108 230.4 R 3.376(,a)-.4 G .875 +(nd the deletion of record number one causes record number \214v)219.684 230.4 +R 3.375(et)-.15 G 3.375(ob)489.93 230.4 S 3.375(er)503.305 230.4 S(enum-)514.45 +230.4 Q .282(bered to record number four)108 242.4 R 2.782(,a)-.4 G 2.782(sw) +231.19 242.4 S .283(ell as the cursor)245.082 242.4 R 2.783(,i)-.4 G 2.783(fp) +316.633 242.4 S .283(ositioned after record number one, to shift do)327.746 +242.4 R .283(wn one)-.25 F(record.)108 254.4 Q .373 +(The recno access method speci\214c data structure pro)108 271.2 R .373 +(vided to)-.15 F F3(dbopen)2.873 E F0 .373(is de\214ned in the include \214le as)-.4 F(follo)108 283.2 Q(ws:)-.25 E(typedef struct {)108 +300 Q(u_long \215ags;)144 312 Q(u_int cachesize;)144 324 Q(u_int psize;)144 336 +Q(int lorder;)144 348 Q(size_t reclen;)144 360 Q(u_char b)144 372 Q -.25(va) +-.15 G(l;).25 E(char *bfname;)144 384 Q 2.5(}R)108 396 S(ECNOINFO;)121.97 396 Q +(The elements of this structure are de\214ned as follo)108 412.8 Q(ws:)-.25 E +14.61(\215ags The)108 429.6 R(\215ag v)2.5 E(alue is speci\214ed by)-.25 E F3 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)313.2 429.6 S(he follo)321.81 +429.6 Q(wing v)-.25 E(alues:)-.25 E(R_FIXEDLEN)144 446.4 Q .962 +(The records are \214x)180 458.4 R .963(ed-length, not byte delimited.)-.15 F +.963(The structure element)5.963 F F3 -.37(re)3.463 G(clen).37 E F0 +(speci\214es)3.463 E .345(the length of the record, and the structure element) +180 470.4 R F3(bval)2.844 E F0 .344(is used as the pad character)2.844 F 5.344 +(.A)-.55 G -.15(ny)530.15 470.4 S .739 +(records, inserted into the database, that are less than)180 482.4 R F3 -.37 +(re)3.239 G(clen).37 E F0 .74(bytes long are automatically)3.239 F(padded.)180 +494.4 Q(R_NOKEY)144 511.2 Q 2.34(In the interf)180 523.2 R 2.34 +(ace speci\214ed by)-.1 F F3(dbopen)4.84 E F0 4.84(,t).24 G 2.34 +(he sequential record retrie)344.98 523.2 R -.25(va)-.25 G 4.84<6c8c>.25 G 2.34 +(lls in both the)478.25 523.2 R(caller')180 535.2 Q 3.556(sk)-.55 G 1.357 -.15 +(ey a)217.336 535.2 T 1.057(nd data structures.).15 F 1.057 +(If the R_NOKEY \215ag is speci\214ed, the)6.057 F F3(cur)3.557 E(sor)-.1 E F0 +(routines)3.557 E .029(are not required to \214ll in the k)180 547.2 R .329 +-.15(ey s)-.1 H 2.529(tructure. This).15 F .028(permits applications to retrie) +2.529 F .328 -.15(ve r)-.25 H .028(ecords at).15 F +(the end of \214les without reading all of the interv)180 559.2 Q +(ening records.)-.15 E(R_SN)144 576 Q(APSHO)-.35 E(T)-.4 E .964 +(This \215ag requires that a snapshot of the \214le be tak)180 588 R .965 +(en when)-.1 F F3(dbopen)3.465 E F0 .965(is called, instead of)3.465 F +(permitting an)180 600 Q 2.5(yu)-.15 G +(nmodi\214ed records to be read from the original \214le.)245.96 600 Q +(cachesize)108 616.8 Q 3.16(As)144 628.8 S .66 +(uggested maximum size, in bytes, of the memory cache.)158.27 628.8 R .659 +(This v)5.659 F .659(alue is)-.25 F F2(only)3.159 E F0(advisory)3.159 E 3.159 +(,a)-.65 G .659(nd the)514.621 628.8 R .046 +(access method will allocate more memory rather than f)144 640.8 R 2.546 +(ail. If)-.1 F F3(cac)2.546 E(hesize)-.15 E F0 2.546(is 0)2.546 F .046 +(\(no size is speci\214ed\) a)2.546 F(def)144 652.8 Q(ault cache is used.)-.1 E +12.95(psize The)108 669.6 R .715 +(recno access method stores the in-memory copies of its records in a btree.) +3.216 F .715(This v)5.715 F .715(alue is the)-.25 F .805 +(size \(in bytes\) of the pages used for nodes in that tree.)144 681.6 R(If) +5.805 E F3(psize)3.305 E F0 .806(is 0 \(no page size is speci\214ed\) a)3.305 F +1.468 +(page size is chosen based on the underlying \214le system I/O block size.)144 +693.6 R(See)6.467 E F3(btr)3.967 E(ee)-.37 E F0 1.467(\(3\) for more).18 F +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 96.815 +(ution August)-.2 F(18, 1994)2.5 E(1)535 732 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 130.12(RECNO\(3\) BSD)72 48 R(Programmer')2.5 E 2.5(sM) +-.55 G 130.12(anual RECNO\(3\))340.17 48 R(information.)144 84 Q 9.62 +(lorder The)108 100.8 R 1.596(byte order for inte)4.096 F 1.596 +(gers in the stored database metadata.)-.15 F 1.597 +(The number should represent the)6.597 F .689(order as an inte)144 112.8 R .689 +(ger; for e)-.15 F .689(xample, big endian order w)-.15 F .689 +(ould be the number 4,321.)-.1 F(If)5.689 E/F1 10/Times-Italic@0 SF(lor)3.189 E +(der)-.37 E F0 .688(is 0 \(no)3.189 F +(order is speci\214ed\) the current host order is used.)144 124.8 Q 9.07 +(reclen The)108 141.6 R(length of a \214x)2.5 E(ed-length record.)-.15 E -.15 +(bv)108 158.4 S 16.68(al The)-.1 F .182 +(delimiting byte to be used to mark the end of a record for v)2.682 F .183 +(ariable-length records, and the pad)-.25 F .809(character for \214x)144 170.4 +R .809(ed-length records.)-.15 F .809(If no v)5.809 F .809 +(alue is speci\214ed, ne)-.25 F .809(wlines \(`)-.25 F(`\\n')-.74 E .808 +('\) are used to mark the)-.74 F(end of v)144 182.4 Q +(ariable-length records and \214x)-.25 E +(ed-length records are padded with spaces.)-.15 E 3.51(bfname The)108 199.2 R +.505 +(recno access method stores the in-memory copies of its records in a btree.) +3.005 F .506(If bfname is non-)5.506 F .065(NULL, it speci\214es the name of t\ +he btree \214le, as if speci\214ed as the \214le name for a dbopen of a btree) +144 211.2 R(\214le.)144 223.2 Q .971(The data part of the k)108 240 R -.15(ey) +-.1 G .972(/data pair used by the recno access method is the same as other acc\ +ess methods.).15 F .199(The k)108 252 R .499 -.15(ey i)-.1 H 2.699(sd).15 G(if) +157.507 252 Q 2.699(ferent. The)-.25 F F1(data)2.699 E F0 .199 +(\214eld of the k)2.699 F .499 -.15(ey s)-.1 H .198 +(hould be a pointer to a memory location of type).15 F F1 -.37(re)2.698 G +(cno_t).37 E F0 2.698(,a).68 G(s)536.11 252 Q .505(de\214ned in the include \214le.)-.4 F .506(This type is normally the lar)5.506 F +.506(gest unsigned inte)-.18 F .506(gral type a)-.15 F -.25(va)-.2 G .506 +(ilable to the).25 F 2.5(implementation. The)108 276 R F1(size)2.5 E F0 +(\214eld of the k)2.5 E .3 -.15(ey s)-.1 H(hould be the size of that type.).15 +E .706(Because there can be no meta-data associated with the underlying recno \ +access method \214les, an)108 292.8 R 3.206(yc)-.15 G(hanges)512.23 292.8 Q +1.262(made to the def)108 304.8 R 1.262(ault v)-.1 F 1.262(alues \(e.g. \214x) +-.25 F 1.263(ed record length or byte separator v)-.15 F 1.263 +(alue\) must be e)-.25 F 1.263(xplicitly speci\214ed)-.15 F +(each time the \214le is opened.)108 316.8 Q .065(In the interf)108 333.6 R +.065(ace speci\214ed by)-.1 F F1(dbopen)2.564 E F0 2.564(,u).24 G .064 +(sing the)261.548 333.6 R F1(put)2.564 E F0(interf)2.564 E .064 +(ace to create a ne)-.1 F 2.564(wr)-.25 G .064 +(ecord will cause the creation of)414.44 333.6 R .755(multiple, empty records \ +if the record number is more than one greater than the lar)108 345.6 R .755 +(gest record currently in)-.18 F(the database.)108 357.6 Q/F2 9/Times-Bold@0 SF +(ERR)72 374.4 Q(ORS)-.27 E F0(The)108 386.4 Q F1 -.37(re)2.922 G(cno).37 E F0 +.421(access method routines may f)2.921 F .421(ail and set)-.1 F F1(errno)2.921 +E F0 .421(for an)2.921 F 2.921(yo)-.15 G 2.921(ft)377.933 386.4 S .421 +(he errors speci\214ed for the library rou-)386.964 386.4 R(tine)108 398.4 Q F1 +(dbopen)2.5 E F0(\(3\) or the follo).24 E(wing:)-.25 E([EINV)108 415.2 Q(AL]) +-1.35 E(An attempt w)144 427.2 Q(as made to add a record to a \214x)-.1 E +(ed-length database that w)-.15 E(as too lar)-.1 E(ge to \214t.)-.18 E F2 +(SEE ALSO)72 444 Q F1(btr)108 456 Q(ee)-.37 E F0(\(3\)).18 E F1(dbopen)2.5 E F0 +(\(3\),).24 E F1(hash)2.5 E F0(\(3\),).28 E F1(mpool)2.5 E F0(\(3\),).51 E F1 +2.754(Document Pr)108 480 R 2.754(ocessing in a Relational Database System)-.45 +F F0 5.255(,M).32 G 2.755(ichael Stonebrak)362.13 480 R(er)-.1 E 5.255(,H)-.4 G +2.755(eidi Stettner)454.06 480 R 5.255(,J)-.4 G(oseph)516.67 480 Q +(Kalash, Antonin Guttman, Nadene L)108 492 Q +(ynn, Memorandum No. UCB/ERL M82/32, May 1982.)-.55 E F2 -.09(BU)72 508.8 S(GS) +.09 E F0(Only big and little endian byte order is supported.)108 520.8 Q +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 96.815 +(ution August)-.2 F(18, 1994)2.5 E(2)535 732 Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/hash/Makefile.inc b/src/util/db2/hash/Makefile.inc new file mode 100644 index 000000000..87746f721 --- /dev/null +++ b/src/util/db2/hash/Makefile.inc @@ -0,0 +1,6 @@ +# @(#)Makefile.inc 8.2 (Berkeley) 11/7/95 + +.PATH: ${.CURDIR}/db/hash + +SRCS+= hash.c hash_bigkey.c hash_buf.c hash_func.c hash_log2.c \ + hash_page.c hsearch.c dbm.c diff --git a/src/util/db2/hash/dbm.c b/src/util/db2/hash/dbm.c new file mode 100644 index 000000000..a5a31f45b --- /dev/null +++ b/src/util/db2/hash/dbm.c @@ -0,0 +1,297 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)dbm.c 8.6 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +#include "db-int.h" + +#include + +#include +#include +#include + +#include "db-ndbm.h" +#include "hash.h" + +/* + * + * This package provides dbm and ndbm compatible interfaces to DB. + * First are the DBM routines, which call the NDBM routines, and + * the NDBM routines, which call the DB routines. + */ +static DBM *__cur_db; + +static void no_open_db __P((void)); + +int +dbminit(file) + char *file; +{ + if (__cur_db != NULL) + (void)dbm_close(__cur_db); + if ((__cur_db = dbm_open(file, O_RDWR, 0)) != NULL) + return (0); + if ((__cur_db = dbm_open(file, O_RDONLY, 0)) != NULL) + return (0); + return (-1); +} + +datum +fetch(key) + datum key; +{ + datum item; + + if (__cur_db == NULL) { + no_open_db(); + item.dptr = 0; + return (item); + } + return (dbm_fetch(__cur_db, key)); +} + +datum +firstkey() +{ + datum item; + + if (__cur_db == NULL) { + no_open_db(); + item.dptr = 0; + return (item); + } + return (dbm_firstkey(__cur_db)); +} + +datum +nextkey(key) + datum key; +{ + datum item; + + if (__cur_db == NULL) { + no_open_db(); + item.dptr = 0; + return (item); + } + return (dbm_nextkey(__cur_db)); +} + +int +delete(key) + datum key; +{ + if (__cur_db == NULL) { + no_open_db(); + return (-1); + } + return (dbm_delete(__cur_db, key)); +} + +int +store(key, dat) + datum key, dat; +{ + if (__cur_db == NULL) { + no_open_db(); + return (-1); + } + return (dbm_store(__cur_db, key, dat, DBM_REPLACE)); +} + +static void +no_open_db() +{ + (void)fprintf(stderr, "dbm: no open database.\n"); +} + +/* + * Returns: + * *DBM on success + * NULL on failure + */ +DBM * +dbm_open(file, flags, mode) + const char *file; + int flags, mode; +{ + HASHINFO info; + char path[MAXPATHLEN]; + + info.bsize = 4096; + info.ffactor = 40; + info.nelem = 1; + info.cachesize = 0; + info.hash = NULL; + info.lorder = 0; + (void)strcpy(path, file); + (void)strcat(path, DBM_SUFFIX); + return ((DBM *)__hash_open(path, flags, mode, &info, 0)); +} + +/* + * Returns: + * Nothing. + */ +void +dbm_close(db) + DBM *db; +{ + (void)(db->close)(db); +} + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_fetch(db, key) + DBM *db; + datum key; +{ + datum retval; + int status; + + status = (db->get)(db, (DBT *)&key, (DBT *)&retval, 0); + if (status) { + retval.dptr = NULL; + retval.dsize = 0; + } + return (retval); +} + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_firstkey(db) + DBM *db; +{ + int status; + datum retdata, retkey; + + status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_FIRST); + if (status) + retkey.dptr = NULL; + return (retkey); +} + +/* + * Returns: + * DATUM on success + * NULL on failure + */ +datum +dbm_nextkey(db) + DBM *db; +{ + int status; + datum retdata, retkey; + + status = (db->seq)(db, (DBT *)&retkey, (DBT *)&retdata, R_NEXT); + if (status) + retkey.dptr = NULL; + return (retkey); +} + +/* + * Returns: + * 0 on success + * <0 failure + */ +int +dbm_delete(db, key) + DBM *db; + datum key; +{ + int status; + + status = (db->del)(db, (DBT *)&key, 0); + if (status) + return (-1); + else + return (0); +} + +/* + * Returns: + * 0 on success + * <0 failure + * 1 if DBM_INSERT and entry exists + */ +int +dbm_store(db, key, content, flags) + DBM *db; + datum key, content; + int flags; +{ + return ((db->put)(db, (DBT *)&key, (DBT *)&content, + (flags == DBM_INSERT) ? R_NOOVERWRITE : 0)); +} + +int +dbm_error(db) + DBM *db; +{ + HTAB *hp; + + hp = (HTAB *)db->internal; + return (hp->errno); +} + +int +dbm_clearerr(db) + DBM *db; +{ + HTAB *hp; + + hp = (HTAB *)db->internal; + hp->errno = 0; + return (0); +} + +int +dbm_dirfno(db) + DBM *db; +{ + return(((HTAB *)db->internal)->fp); +} diff --git a/src/util/db2/hash/extern.h b/src/util/db2/hash/extern.h new file mode 100644 index 000000000..dd8a34e8e --- /dev/null +++ b/src/util/db2/hash/extern.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)extern.h 8.8 (Berkeley) 11/7/95 + */ + +PAGE16 *__add_bigpage __P((HTAB *, PAGE16 *, indx_t, const u_int8_t)); +PAGE16 *__add_ovflpage __P((HTAB *, PAGE16 *)); +int32_t __addel __P((HTAB *, ITEM_INFO *, + const DBT *, const DBT *, u_int32_t, const u_int8_t)); +u_int32_t __alloc_tmp __P((HTAB*)); +int32_t __big_delete __P((HTAB *, PAGE16 *, indx_t)); +int32_t __big_insert __P((HTAB *, PAGE16 *, const DBT *, const DBT *)); +int32_t __big_keydata __P((HTAB *, PAGE16 *, DBT *, DBT *, int32_t)); +int32_t __big_return __P((HTAB *, ITEM_INFO *, DBT *, int32_t)); +u_int32_t __call_hash __P((HTAB *, int8_t *, int32_t)); +CURSOR *__cursor_creat __P((const DB *)); +int32_t __delete_page __P((HTAB *, PAGE16 *, int32_t)); +int32_t __delpair __P((HTAB *, CURSOR *, ITEM_INFO *)); +int32_t __expand_table __P((HTAB *)); +int32_t __find_bigpair __P((HTAB *, CURSOR *, int8_t *, int32_t)); +void __free_ovflpage __P((HTAB *, PAGE16 *)); +int32_t __get_bigkey __P((HTAB *, PAGE16 *, indx_t, DBT *)); +PAGE16 *__get_buf __P((HTAB *, u_int32_t, int32_t)); +u_int32_t __get_item __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); +u_int32_t __get_item_done __P((HTAB *, CURSOR *)); +u_int32_t __get_item_first __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); +u_int32_t __get_item_next __P((HTAB *, CURSOR *, DBT *, DBT *, ITEM_INFO *)); +u_int32_t __get_item_reset __P((HTAB *, CURSOR *)); +PAGE16 *__get_page __P((HTAB *, u_int32_t, int32_t)); +int32_t __ibitmap __P((HTAB *, int32_t, int32_t, int32_t)); +u_int32_t __log2 __P((u_int32_t)); +int32_t __new_page __P((HTAB *, u_int32_t, int32_t)); +void __pgin_routine __P((void *, db_pgno_t, void *)); +void __pgout_routine __P((void *, db_pgno_t, void *)); +u_int32_t __put_buf __P((HTAB *, PAGE16 *, u_int32_t)); +int32_t __put_page __P((HTAB *, PAGE16 *, int32_t, int32_t)); +void __reclaim_tmp __P((HTAB *)); +int32_t __split_page __P((HTAB *, u_int32_t, u_int32_t)); + +/* Default hash routine. */ +extern u_int32_t (*__default_hash) __P((const void *, size_t)); + +#ifdef HASH_STATISTICS +extern long hash_accesses, hash_bigpages, hash_collisions, hash_expansions; +extern long hash_overflow; +#endif diff --git a/src/util/db2/hash/hash.c b/src/util/db2/hash/hash.c new file mode 100644 index 000000000..017138606 --- /dev/null +++ b/src/util/db2/hash/hash.c @@ -0,0 +1,1068 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash.c 8.12 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include "db-int.h" +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int32_t flush_meta __P((HTAB *)); +static int32_t hash_access __P((HTAB *, ACTION, DBT *, DBT *)); +static int32_t hash_close __P((DB *)); +static int32_t hash_delete __P((const DB *, const DBT *, u_int32_t)); +static int32_t hash_fd __P((const DB *)); +static int32_t hash_get __P((const DB *, const DBT *, DBT *, u_int32_t)); +static int32_t hash_put __P((const DB *, DBT *, const DBT *, u_int32_t)); +static int32_t hash_seq __P((const DB *, DBT *, DBT *, u_int32_t)); +static int32_t hash_sync __P((const DB *, u_int32_t)); +static int32_t hdestroy __P((HTAB *)); +static int32_t cursor_get __P((const DB *, CURSOR *, DBT *, DBT *, \ + u_int32_t)); +static int32_t cursor_delete __P((const DB *, CURSOR *, u_int32_t)); +static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *)); +static int32_t init_htab __P((HTAB *, int32_t)); +#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN +static void swap_header __P((HTAB *)); +static void swap_header_copy __P((HASHHDR *, HASHHDR *)); +#endif +static u_int32_t hget_header __P((HTAB *, u_int32_t)); +static void hput_header __P((HTAB *)); + +#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } + +/* Return values */ +#define SUCCESS (0) +#define ERROR (-1) +#define ABNORMAL (1) + +#ifdef HASH_STATISTICS +u_int32_t hash_accesses, hash_collisions, hash_expansions, hash_overflows, + hash_bigpages; +#endif + +/************************** INTERFACE ROUTINES ***************************/ +/* OPEN/CLOSE */ + +extern DB * +__hash_open(file, flags, mode, info, dflags) + const char *file; + int32_t flags, mode, dflags; + const HASHINFO *info; /* Special directives for create */ +{ + struct stat statbuf; + DB *dbp; + DBT mpool_key; + HTAB *hashp; + int32_t bpages, csize, new_table, save_errno, specified_file; + + if ((flags & O_ACCMODE) == O_WRONLY) { + errno = EINVAL; + return (NULL); + } + if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB)))) + return (NULL); + hashp->fp = -1; + + /* set this now, before file goes away... */ + specified_file = (file != NULL); + if (!file) { + file = tmpnam(NULL); + /* store the file name so that we can unlink it later */ + hashp->fname = (char *)file; +#ifdef DEBUG + fprintf(stderr, "Using file name %s.\n", file); +#endif + } + /* + * Even if user wants write only, we need to be able to read + * the actual file, so we need to open it read/write. But, the + * field in the hashp structure needs to be accurate so that + * we can check accesses. + */ + hashp->flags = flags; + hashp->save_file = specified_file && (hashp->flags & O_RDWR); + + new_table = 0; + if (!file || (flags & O_TRUNC) || + (stat(file, &statbuf) && (errno == ENOENT))) { + if (errno == ENOENT) + errno = 0; /* In case someone looks at errno. */ + new_table = 1; + } + if (file) { + if ((hashp->fp = open(file, flags, mode)) == -1) + RETURN_ERROR(errno, error0); + (void)fcntl(hashp->fp, F_SETFD, 1); + } + + /* Process arguments to set up hash table header. */ + if (new_table) { + if (!(hashp = init_hash(hashp, file, (HASHINFO *)info))) + RETURN_ERROR(errno, error1); + } else { + /* Table already exists */ + if (info && info->hash) + hashp->hash = info->hash; + else + hashp->hash = __default_hash; + + /* copy metadata from page into header */ + if (hget_header(hashp, + (info && info->bsize ? info->bsize : DEF_BUCKET_SIZE)) != + sizeof(HASHHDR)) + RETURN_ERROR(EFTYPE, error1); + + /* Verify file type, versions and hash function */ + if (hashp->hdr.magic != HASHMAGIC) + RETURN_ERROR(EFTYPE, error1); +#define OLDHASHVERSION 1 + if (hashp->hdr.version != HASHVERSION && + hashp->hdr.version != OLDHASHVERSION) + RETURN_ERROR(EFTYPE, error1); + if (hashp->hash(CHARKEY, sizeof(CHARKEY)) + != hashp->hdr.h_charkey) + RETURN_ERROR(EFTYPE, error1); + /* + * Figure out how many segments we need. Max_Bucket is the + * maximum bucket number, so the number of buckets is + * max_bucket + 1. + */ + + /* Read in bitmaps */ + bpages = (hashp->hdr.spares[hashp->hdr.ovfl_point] + + (hashp->hdr.bsize << BYTE_SHIFT) - 1) >> + (hashp->hdr.bshift + BYTE_SHIFT); + + hashp->nmaps = bpages; + (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *)); + } + + /* start up mpool */ + mpool_key.data = (u_int8_t *)file; + mpool_key.size = strlen(file); + + if (info && info->cachesize) + csize = info->cachesize / hashp->hdr.bsize; + else + csize = DEF_CACHESIZE / hashp->hdr.bsize; + hashp->mp = mpool_open(&mpool_key, hashp->fp, hashp->hdr.bsize, csize); + + if (!hashp->mp) + RETURN_ERROR(errno, error1); + mpool_filter(hashp->mp, __pgin_routine, __pgout_routine, hashp); + + /* + * For a new table, set up the bitmaps. + */ + if (new_table && + init_htab(hashp, info && info->nelem ? info->nelem : 1)) + goto error2; + + /* initialize the cursor queue */ + TAILQ_INIT(&hashp->curs_queue); + hashp->seq_cursor = NULL; + + + /* get a chunk of memory for our split buffer */ + hashp->split_buf = (PAGE16 *)malloc(hashp->hdr.bsize); + if (!hashp->split_buf) + goto error2; + + hashp->new_file = new_table; + + if (!(dbp = (DB *)malloc(sizeof(DB)))) + goto error2; + + dbp->internal = hashp; + dbp->close = hash_close; + dbp->del = hash_delete; + dbp->fd = hash_fd; + dbp->get = hash_get; + dbp->put = hash_put; + dbp->seq = hash_seq; + dbp->sync = hash_sync; + dbp->type = DB_HASH; + +#ifdef DEBUG + (void)fprintf(stderr, + "%s\n%s%lx\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", + "init_htab:", + "TABLE POINTER ", (void *)hashp, + "BUCKET SIZE ", hashp->hdr.bsize, + "BUCKET SHIFT ", hashp->hdr.bshift, + "FILL FACTOR ", hashp->hdr.ffactor, + "MAX BUCKET ", hashp->hdr.max_bucket, + "OVFL POINT ", hashp->hdr.ovfl_point, + "LAST FREED ", hashp->hdr.last_freed, + "HIGH MASK ", hashp->hdr.high_mask, + "LOW MASK ", hashp->hdr.low_mask, + "NKEYS ", hashp->hdr.nkeys); +#endif +#ifdef HASH_STATISTICS + hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; + hash_bigpages = 0; +#endif + return (dbp); + +error2: + save_errno = errno; + hdestroy(hashp); + errno = save_errno; + return (NULL); + +error1: + if (hashp != NULL) + (void)close(hashp->fp); + +error0: + free(hashp); + errno = save_errno; + return (NULL); +} + +static int32_t +hash_close(dbp) + DB *dbp; +{ + HTAB *hashp; + int32_t retval; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + retval = hdestroy(hashp); + free(dbp); + return (retval); +} + +static int32_t +hash_fd(dbp) + const DB *dbp; +{ + HTAB *hashp; + + if (!dbp) + return (ERROR); + + hashp = (HTAB *)dbp->internal; + if (hashp->fp == -1) { + errno = ENOENT; + return (-1); + } + return (hashp->fp); +} + +/************************** LOCAL CREATION ROUTINES **********************/ +static HTAB * +init_hash(hashp, file, info) + HTAB *hashp; + const char *file; + HASHINFO *info; +{ + struct stat statbuf; + int32_t nelem; + + nelem = 1; + hashp->hdr.nkeys = 0; + hashp->hdr.lorder = DB_BYTE_ORDER; + hashp->hdr.bsize = DEF_BUCKET_SIZE; + hashp->hdr.bshift = DEF_BUCKET_SHIFT; + hashp->hdr.ffactor = DEF_FFACTOR; + hashp->hash = __default_hash; + memset(hashp->hdr.spares, 0, sizeof(hashp->hdr.spares)); + memset(hashp->hdr.bitmaps, 0, sizeof(hashp->hdr.bitmaps)); + + /* Fix bucket size to be optimal for file system */ + if (file != NULL) { + if (stat(file, &statbuf)) + return (NULL); + hashp->hdr.bsize = statbuf.st_blksize; + hashp->hdr.bshift = __log2(hashp->hdr.bsize); + } + if (info) { + if (info->bsize) { + /* Round pagesize up to power of 2 */ + hashp->hdr.bshift = __log2(info->bsize); + hashp->hdr.bsize = 1 << hashp->hdr.bshift; + if (hashp->hdr.bsize > MAX_BSIZE) { + errno = EINVAL; + return (NULL); + } + } + if (info->ffactor) + hashp->hdr.ffactor = info->ffactor; + if (info->hash) + hashp->hash = info->hash; + if (info->lorder) { + if ((info->lorder != DB_BIG_ENDIAN) && + (info->lorder != DB_LITTLE_ENDIAN)) { + errno = EINVAL; + return (NULL); + } + hashp->hdr.lorder = info->lorder; + } + } + return (hashp); +} + +/* + * Returns 0 on No Error + */ +static int32_t +init_htab(hashp, nelem) + HTAB *hashp; + int32_t nelem; +{ + int32_t l2, nbuckets; + db_pgno_t i; + + /* + * Divide number of elements by the fill factor and determine a + * desired number of buckets. Allocate space for the next greater + * power of two number of buckets. + */ + nelem = (nelem - 1) / hashp->hdr.ffactor + 1; + + l2 = __log2(MAX(nelem, 2)); + nbuckets = 1 << l2; + + hashp->hdr.spares[l2] = l2 + 1; + hashp->hdr.spares[l2 + 1] = l2 + 1; + hashp->hdr.ovfl_point = l2; + hashp->hdr.last_freed = 2; + + hashp->hdr.max_bucket = hashp->hdr.low_mask = nbuckets - 1; + hashp->hdr.high_mask = (nbuckets << 1) - 1; + + /* + * The number of header pages is the size of the header divided by + * the amount of freespace on header pages (the page size - the + * size of 1 integer where the length of the header info on that + * page is stored) plus another page if it didn't divide evenly. + */ + hashp->hdr.hdrpages = + (sizeof(HASHHDR) / (hashp->hdr.bsize - HEADER_OVERHEAD)) + + (((sizeof(HASHHDR) % (hashp->hdr.bsize - HEADER_OVERHEAD)) == 0) + ? 0 : 1); + + /* Create pages for these buckets */ + /* + for (i = 0; i <= hashp->hdr.max_bucket; i++) { + if (__new_page(hashp, (u_int32_t)i, A_BUCKET) != 0) + return (-1); + } + */ + + /* First bitmap page is at: splitpoint l2 page offset 1 */ + if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) + return (-1); + + return (0); +} + +/* + * Functions to get/put hash header. We access the file directly. + */ +u_int32_t +hget_header(hashp, page_size) + HTAB *hashp; + u_int32_t page_size; +{ + u_int32_t num_copied, i; + u_int8_t *hdr_dest; + + num_copied = 0; + i = 0; + + hdr_dest = (u_int8_t *)&hashp->hdr; + + /* + * XXX + * This should not be printing to stderr on a "normal" error case. + */ + lseek(hashp->fp, 0, SEEK_SET); + num_copied = read(hashp->fp, hdr_dest, sizeof(HASHHDR)); + if (num_copied != sizeof(HASHHDR)) { + fprintf(stderr, "hash: could not retrieve header"); + return (0); + } +#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN + swap_header(hashp); +#endif + return (num_copied); +} + +void +hput_header(hashp) + HTAB *hashp; +{ + HASHHDR *whdrp; +#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN + HASHHDR whdr; +#endif + u_int32_t num_copied, i; + + num_copied = i = 0; + + whdrp = &hashp->hdr; +#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN + whdrp = &whdr; + swap_header_copy(&hashp->hdr, whdrp); +#endif + + lseek(hashp->fp, 0, SEEK_SET); + num_copied = write(hashp->fp, whdrp, sizeof(HASHHDR)); + if (num_copied != sizeof(HASHHDR)) + (void)fprintf(stderr, "hash: could not write hash header"); + return; +} + +/********************** DESTROY/CLOSE ROUTINES ************************/ + +/* + * Flushes any changes to the file if necessary and destroys the hashp + * structure, freeing all allocated space. + */ +static int32_t +hdestroy(hashp) + HTAB *hashp; +{ + int32_t save_errno; + + save_errno = 0; + +#ifdef HASH_STATISTICS + { int i; + (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", + hash_accesses, hash_collisions); + (void)fprintf(stderr, + "hdestroy: expansions %ld\n", hash_expansions); + (void)fprintf(stderr, + "hdestroy: overflows %ld\n", hash_overflows); + (void)fprintf(stderr, + "hdestroy: big key/data pages %ld\n", hash_bigpages); + (void)fprintf(stderr, + "keys %ld maxp %d\n", hashp->hdr.nkeys, hashp->hdr.max_bucket); + + for (i = 0; i < NCACHED; i++) + (void)fprintf(stderr, + "spares[%d] = %d\n", i, hashp->hdr.spares[i]); + } +#endif + + if (flush_meta(hashp) && !save_errno) + save_errno = errno; + + /* Free the split page */ + if (hashp->split_buf) + free(hashp->split_buf); + + /* Free the big key and big data returns */ + if (hashp->bigkey_buf) + free(hashp->bigkey_buf); + if (hashp->bigdata_buf) + free(hashp->bigdata_buf); + + /* XXX This should really iterate over the cursor queue, but + it's not clear how to do that, and the only cursor a hash + table ever creates is the one used by hash_seq(). Passing + NULL as the first arg is also a kludge, but I know that + it's never used, so I do it. The intent is to plug the + memory leak. Correctness can come later. */ + + if (hashp->seq_cursor) + hashp->seq_cursor->delete(NULL, hashp->seq_cursor, 0); + + /* shut down mpool */ + mpool_sync(hashp->mp); + mpool_close(hashp->mp); + + if (hashp->fp != -1) + (void)close(hashp->fp); + + /* + * *** This may cause problems if hashp->fname is set in any case + * other than the case that we are generating a temporary file name. + * Note that the new version of mpool should support temporary + * files within mpool itself. + */ + if (hashp->fname && !hashp->save_file) { +#ifdef DEBUG + fprintf(stderr, "Unlinking file %s.\n", hashp->fname); +#endif + /* we need to chmod the file to allow it to be deleted... */ + chmod(hashp->fname, 0700); + unlink(hashp->fname); + /* destroy the temporary name */ + tmpnam(NULL); + } + free(hashp); + + if (save_errno) { + errno = save_errno; + return (ERROR); + } + return (SUCCESS); +} + +/* + * Write modified pages to disk + * + * Returns: + * 0 == OK + * -1 ERROR + */ +static int32_t +hash_sync(dbp, flags) + const DB *dbp; + u_int32_t flags; +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + + /* + * XXX + * Check success/failure conditions. + */ + return (flush_meta(hashp) || mpool_sync(hashp->mp)); +} + +/* + * Returns: + * 0 == OK + * -1 indicates that errno should be set + */ +static int32_t +flush_meta(hashp) + HTAB *hashp; +{ + int32_t i; + + if (!hashp->save_file) + return (0); + hashp->hdr.magic = HASHMAGIC; + hashp->hdr.version = HASHVERSION; + hashp->hdr.h_charkey = hashp->hash(CHARKEY, sizeof(CHARKEY)); + + /* write out metadata */ + hput_header(hashp); + + for (i = 0; i < NCACHED; i++) + if (hashp->mapp[i]) { + if (__put_page(hashp, + (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) + return (-1); + hashp->mapp[i] = NULL; + } + return (0); +} + +/*******************************SEARCH ROUTINES *****************************/ +/* + * All the access routines return + * + * Returns: + * 0 on SUCCESS + * 1 to indicate an external ERROR (i.e. key not found, etc) + * -1 to indicate an internal ERROR (i.e. out of memory, etc) + */ + +/* *** make sure this is true! */ + +static int32_t +hash_get(dbp, key, data, flag) + const DB *dbp; + const DBT *key; + DBT *data; + u_int32_t flag; +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + return (hash_access(hashp, HASH_GET, (DBT *)key, data)); +} + +static int32_t +hash_put(dbp, key, data, flag) + const DB *dbp; + DBT *key; + const DBT *data; + u_int32_t flag; +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag && flag != R_NOOVERWRITE) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->errno = errno = EPERM; + return (ERROR); + } + return (hash_access(hashp, flag == R_NOOVERWRITE ? + HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); +} + +static int32_t +hash_delete(dbp, key, flag) + const DB *dbp; + const DBT *key; + u_int32_t flag; /* Ignored */ +{ + HTAB *hashp; + + hashp = (HTAB *)dbp->internal; + if (flag) { + hashp->errno = errno = EINVAL; + return (ERROR); + } + if ((hashp->flags & O_ACCMODE) == O_RDONLY) { + hashp->errno = errno = EPERM; + return (ERROR); + } + + return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); +} + +/* + * Assume that hashp has been set in wrapper routine. + */ +static int32_t +hash_access(hashp, action, key, val) + HTAB *hashp; + ACTION action; + DBT *key, *val; +{ + DBT page_key, page_val; + CURSOR cursor; + ITEM_INFO item_info; + u_int32_t bucket; + u_int32_t num_items; + +#ifdef HASH_STATISTICS + hash_accesses++; +#endif + + num_items = 0; + + /* + * Set up item_info so that we're looking for space to add an item + * as we cycle through the pages looking for the key. + */ + if (action == HASH_PUT || action == HASH_PUTNEW) { + if (ISBIG(key->size + val->size, hashp)) + item_info.seek_size = PAIR_OVERHEAD; + else + item_info.seek_size = key->size + val->size; + } else + item_info.seek_size = 0; + item_info.seek_found_page = 0; + + bucket = __call_hash(hashp, (int8_t *)key->data, key->size); + + cursor.pagep = NULL; + __get_item_reset(hashp, &cursor); + + cursor.bucket = bucket; + while (1) { + __get_item_next(hashp, &cursor, &page_key, &page_val, &item_info); + if (item_info.status == ITEM_ERROR) + return (ABNORMAL); + if (item_info.status == ITEM_NO_MORE) + break; + num_items++; + if (item_info.key_off == BIGPAIR) { + /* + * !!! + * 0 is a valid index. + */ + if (__find_bigpair(hashp, &cursor, (int8_t *)key->data, + key->size) > 0) + goto found; + } else if (key->size == page_key.size && + !memcmp(key->data, page_key.data, key->size)) + goto found; + } +#ifdef HASH_STATISTICS + hash_collisions++; +#endif + __get_item_done(hashp, &cursor); + + /* + * At this point, item_info will list either the last page in + * the chain, or the last page in the chain plus a pgno for where + * to find the first page in the chain with space for the + * item we wish to add. + */ + + /* Not found */ + switch (action) { + case HASH_PUT: + case HASH_PUTNEW: + if (__addel(hashp, &item_info, key, val, num_items, 0)) + return (ERROR); + break; + case HASH_GET: + case HASH_DELETE: + default: + return (ABNORMAL); + } + + if (item_info.caused_expand) + __expand_table(hashp); + return (SUCCESS); + +found: __get_item_done(hashp, &cursor); + + switch (action) { + case HASH_PUTNEW: + /* mpool_put(hashp->mp, pagep, 0); */ + return (ABNORMAL); + case HASH_GET: + if (item_info.key_off == BIGPAIR) { + if (__big_return(hashp, &item_info, val, 0)) + return (ERROR); + } else { + val->data = page_val.data; + val->size = page_val.size; + } + /* *** data may not be available! */ + break; + case HASH_PUT: + if (__delpair(hashp, &cursor, &item_info) || + __addel(hashp, &item_info, key, val, UNKNOWN, 0)) + return (ERROR); + __get_item_done(hashp, &cursor); + if (item_info.caused_expand) + __expand_table(hashp); + break; + case HASH_DELETE: + if (__delpair(hashp, &cursor, &item_info)) + return (ERROR); + break; + default: + abort(); + } + return (SUCCESS); +} + +/* ****************** CURSORS ********************************** */ +CURSOR * +__cursor_creat(dbp) + const DB *dbp; +{ + CURSOR *new_curs; + HTAB *hashp; + + new_curs = (CURSOR *)malloc(sizeof(struct cursor_t)); + if (!new_curs) + return NULL; + new_curs->internal = + (struct item_info *)malloc(sizeof(struct item_info)); + if (!new_curs->internal) { + free(new_curs); + return NULL; + } + new_curs->get = cursor_get; + new_curs->delete = cursor_delete; + + new_curs->bucket = 0; + new_curs->pgno = INVALID_PGNO; + new_curs->ndx = 0; + new_curs->pgndx = 0; + new_curs->pagep = NULL; + + /* place onto queue of cursors */ + hashp = (HTAB *)dbp->internal; + TAILQ_INSERT_TAIL(&hashp->curs_queue, new_curs, queue); + + return new_curs; +} + +int32_t +cursor_get(dbp, cursorp, key, val, flags) + const DB *dbp; + CURSOR *cursorp; + DBT *key, *val; + u_int32_t flags; +{ + HTAB *hashp; + ITEM_INFO item_info; + + hashp = (HTAB *)dbp->internal; + + if (flags && flags != R_FIRST && flags != R_NEXT) { + hashp->errno = errno = EINVAL; + return (ERROR); + } +#ifdef HASH_STATISTICS + hash_accesses++; +#endif + + item_info.seek_size = 0; + + if (flags == R_FIRST) + __get_item_first(hashp, cursorp, key, val, &item_info); + else + __get_item_next(hashp, cursorp, key, val, &item_info); + + /* + * This needs to be changed around. As is, get_item_next advances + * the pointers on the page but this function actually advances + * bucket pointers. This works, since the only other place we + * use get_item_next is in hash_access which only deals with one + * bucket at a time. However, there is the problem that certain other + * functions (such as find_bigpair and delpair) depend on the + * pgndx member of the cursor. Right now, they are using pngdx - 1 + * since indices refer to the __next__ item that is to be fetched + * from the page. This is ugly, as you may have noticed, whoever + * you are. The best solution would be to depend on item_infos to + * deal with _current_ information, and have the cursors only + * deal with _next_ information. In that scheme, get_item_next + * would also advance buckets. Version 3... + */ + + + /* + * Must always enter this loop to do error handling and + * check for big key/data pair. + */ + while (1) { + if (item_info.status == ITEM_OK) { + if (item_info.key_off == BIGPAIR && + __big_keydata(hashp, cursorp->pagep, key, val, + item_info.pgndx)) + return (ABNORMAL); + + break; + } else if (item_info.status != ITEM_NO_MORE) + return (ABNORMAL); + + __put_page(hashp, cursorp->pagep, A_RAW, 0); + cursorp->ndx = cursorp->pgndx = 0; + cursorp->bucket++; + cursorp->pgno = INVALID_PGNO; + cursorp->pagep = NULL; + if (cursorp->bucket > hashp->hdr.max_bucket) + return (ABNORMAL); + __get_item_next(hashp, cursorp, key, val, &item_info); + } + + __get_item_done(hashp, cursorp); + return (0); +} + +int32_t +cursor_delete(dbp, cursor, flags) + const DB *dbp; + CURSOR *cursor; + u_int32_t flags; +{ + /* XXX this is empirically determined, so it might not be completely + correct, but it seems to work. At the very least it fixes + a memory leak */ + + free(cursor->internal); + free(cursor); + + return (0); +} + +static int32_t +hash_seq(dbp, key, val, flag) + const DB *dbp; + DBT *key, *val; + u_int32_t flag; +{ + HTAB *hashp; + + /* + * Seq just uses the default cursor to go sequecing through the + * database. Note that the default cursor is the first in the list. + */ + + hashp = (HTAB *)dbp->internal; + if (!hashp->seq_cursor) + hashp->seq_cursor = __cursor_creat(dbp); + + return (hashp->seq_cursor->get(dbp, hashp->seq_cursor, key, val, flag)); +} + +/********************************* UTILITIES ************************/ + +/* + * Returns: + * 0 ==> OK + * -1 ==> Error + */ +int32_t +__expand_table(hashp) + HTAB *hashp; +{ + u_int32_t old_bucket, new_bucket; + int32_t spare_ndx; + +#ifdef HASH_STATISTICS + hash_expansions++; +#endif + new_bucket = ++hashp->hdr.max_bucket; + old_bucket = (hashp->hdr.max_bucket & hashp->hdr.low_mask); + + /* Get a page for this new bucket */ + if (__new_page(hashp, new_bucket, A_BUCKET) != 0) + return (-1); + + /* + * If the split point is increasing (hdr.max_bucket's log base 2 + * increases), we need to copy the current contents of the spare + * split bucket to the next bucket. + */ + spare_ndx = __log2(hashp->hdr.max_bucket + 1); + if (spare_ndx > hashp->hdr.ovfl_point) { + hashp->hdr.spares[spare_ndx] = hashp->hdr.spares[hashp->hdr.ovfl_point]; + hashp->hdr.ovfl_point = spare_ndx; + } + if (new_bucket > hashp->hdr.high_mask) { + /* Starting a new doubling */ + hashp->hdr.low_mask = hashp->hdr.high_mask; + hashp->hdr.high_mask = new_bucket | hashp->hdr.low_mask; + } + if (BUCKET_TO_PAGE(new_bucket) > MAX_PAGES(hashp)) { + fprintf(stderr, "hash: Cannot allocate new bucket. Pages exhausted.\n"); + return (-1); + } + /* Relocate records to the new bucket */ + return (__split_page(hashp, old_bucket, new_bucket)); +} + +u_int32_t +__call_hash(hashp, k, len) + HTAB *hashp; + int8_t *k; + int32_t len; +{ + int32_t n, bucket; + + n = hashp->hash(k, len); + bucket = n & hashp->hdr.high_mask; + if (bucket > hashp->hdr.max_bucket) + bucket = bucket & hashp->hdr.low_mask; + return (bucket); +} + +#if DB_BYTE_ORDER == DB_LITTLE_ENDIAN +/* + * Hashp->hdr needs to be byteswapped. + */ +static void +swap_header_copy(srcp, destp) + HASHHDR *srcp, *destp; +{ + int32_t i; + + P_32_COPY(srcp->magic, destp->magic); + P_32_COPY(srcp->version, destp->version); + P_32_COPY(srcp->lorder, destp->lorder); + P_32_COPY(srcp->bsize, destp->bsize); + P_32_COPY(srcp->bshift, destp->bshift); + P_32_COPY(srcp->ovfl_point, destp->ovfl_point); + P_32_COPY(srcp->last_freed, destp->last_freed); + P_32_COPY(srcp->max_bucket, destp->max_bucket); + P_32_COPY(srcp->high_mask, destp->high_mask); + P_32_COPY(srcp->low_mask, destp->low_mask); + P_32_COPY(srcp->ffactor, destp->ffactor); + P_32_COPY(srcp->nkeys, destp->nkeys); + P_32_COPY(srcp->hdrpages, destp->hdrpages); + P_32_COPY(srcp->h_charkey, destp->h_charkey); + for (i = 0; i < NCACHED; i++) { + P_32_COPY(srcp->spares[i], destp->spares[i]); + P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); + } +} + +static void +swap_header(hashp) + HTAB *hashp; +{ + HASHHDR *hdrp; + int32_t i; + + hdrp = &hashp->hdr; + + M_32_SWAP(hdrp->magic); + M_32_SWAP(hdrp->version); + M_32_SWAP(hdrp->lorder); + M_32_SWAP(hdrp->bsize); + M_32_SWAP(hdrp->bshift); + M_32_SWAP(hdrp->ovfl_point); + M_32_SWAP(hdrp->last_freed); + M_32_SWAP(hdrp->max_bucket); + M_32_SWAP(hdrp->high_mask); + M_32_SWAP(hdrp->low_mask); + M_32_SWAP(hdrp->ffactor); + M_32_SWAP(hdrp->nkeys); + M_32_SWAP(hdrp->hdrpages); + M_32_SWAP(hdrp->h_charkey); + for (i = 0; i < NCACHED; i++) { + M_32_SWAP(hdrp->spares[i]); + M_16_SWAP(hdrp->bitmaps[i]); + } +} +#endif /* DB_BYTE_ORDER == DB_LITTLE_ENDIAN */ diff --git a/src/util/db2/hash/hash.c.patch b/src/util/db2/hash/hash.c.patch new file mode 100644 index 000000000..b72cc0d26 --- /dev/null +++ b/src/util/db2/hash/hash.c.patch @@ -0,0 +1,109 @@ +*** /tmp/,RCSt1a21714 Wed Apr 3 11:49:15 1996 +--- hash.c Wed Apr 3 08:43:04 1996 +*************** +*** 399,405 + /* Create pages for these buckets */ + /* + for (i = 0; i <= hashp->hdr.max_bucket; i++) { +! if (__new_page(hashp, i, A_BUCKET) != 0) + return (-1); + } + */ + +--- 399,405 ----- + /* Create pages for these buckets */ + /* + for (i = 0; i <= hashp->hdr.max_bucket; i++) { +! if (__new_page(hashp, (u_int32_t)i, A_BUCKET) != 0) + return (-1); + } + */ +*************** +*** 560,567 + * XXX + * Check success/failure conditions. + */ +! mpool_sync(hashp->mp); +! return (0); + } + + /* + +--- 560,566 ----- + * XXX + * Check success/failure conditions. + */ +! return (flush_meta(hashp) || mpool_sync(hashp->mp)); + } + + /* +*************** +*** 585,591 + hput_header(hashp); + + for (i = 0; i < NCACHED; i++) +! if (hashp->mapp[i]) + if (__put_page(hashp, + (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) + return (-1); + +--- 584,590 ----- + hput_header(hashp); + + for (i = 0; i < NCACHED; i++) +! if (hashp->mapp[i]) { + if (__put_page(hashp, + (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) + return (-1); +*************** +*** 589,594 + if (__put_page(hashp, + (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) + return (-1); + return (0); + } + + +--- 588,595 ----- + if (__put_page(hashp, + (PAGE16 *)hashp->mapp[i], A_BITMAP, 1)) + return (-1); ++ hashp->mapp[i] = NULL; ++ } + return (0); + } + +*************** +*** 726,732 + #ifdef HASH_STATISTICS + hash_collisions++; + #endif +- + __get_item_done(hashp, &cursor); + + /* + +--- 727,732 ----- + #ifdef HASH_STATISTICS + hash_collisions++; + #endif + __get_item_done(hashp, &cursor); + + /* +*************** +*** 773,778 + if (__delpair(hashp, &cursor, &item_info) || + __addel(hashp, &item_info, key, val, UNKNOWN, 0)) + return (ERROR); + if (item_info.caused_expand) + __expand_table(hashp); + break; + +--- 773,779 ----- + if (__delpair(hashp, &cursor, &item_info) || + __addel(hashp, &item_info, key, val, UNKNOWN, 0)) + return (ERROR); ++ __get_item_done(hashp, &cursor); + if (item_info.caused_expand) + __expand_table(hashp); + break; diff --git a/src/util/db2/hash/hash.h b/src/util/db2/hash/hash.h new file mode 100644 index 000000000..973d543a8 --- /dev/null +++ b/src/util/db2/hash/hash.h @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)hash.h 8.4 (Berkeley) 11/2/95 + */ + +#include "mpool.h" +#include "db-queue.h" + +/* Operations */ +typedef enum { + HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT +} ACTION; + +/* cursor structure */ +typedef struct cursor_t { + TAILQ_ENTRY(cursor_t) queue; + int (*get) __P((const DB *, struct cursor_t *, DBT *, DBT *, \ + u_int32_t)); + int (*delete) __P((const DB *, struct cursor_t *, u_int32_t)); + db_pgno_t bucket; + db_pgno_t pgno; + indx_t ndx; + indx_t pgndx; + u_int16_t *pagep; + void *internal; +} CURSOR; + + +#define IS_BUCKET(X) ((X) & BUF_BUCKET) +#define IS_VALID(X) (!((X) & BUF_INVALID)) + +/* Hash Table Information */ +typedef struct hashhdr { /* Disk resident portion */ + int32_t magic; /* Magic NO for hash tables */ + int32_t version; /* Version ID */ + int32_t lorder; /* Byte Order */ + int32_t bsize; /* Bucket/Page Size */ + int32_t bshift; /* Bucket shift */ + int32_t ovfl_point; /* Where overflow pages are being allocated */ + int32_t last_freed; /* Last overflow page freed */ + int32_t max_bucket; /* ID of Maximum bucket in use */ + int32_t high_mask; /* Mask to modulo into entire table */ + int32_t low_mask; /* Mask to modulo into lower half of table */ + int32_t ffactor; /* Fill factor */ + int32_t nkeys; /* Number of keys in hash table */ + int32_t hdrpages; /* Size of table header */ + int32_t h_charkey; /* value of hash(CHARKEY) */ +#define NCACHED 32 /* number of bit maps and spare points */ + int32_t spares[NCACHED];/* spare pages for overflow */ + u_int16_t bitmaps[NCACHED]; /* address of overflow page bitmaps */ +} HASHHDR; + +typedef struct htab { /* Memory resident data structure */ + TAILQ_HEAD(_cursor_queue, cursor_t) curs_queue; + HASHHDR hdr; /* Header */ + u_int32_t (*hash) __P((const void *, size_t)); /* Hash Function */ + int32_t flags; /* Flag values */ + int32_t fp; /* File pointer */ + char *fname; /* File path */ + u_int8_t *bigdata_buf; /* Temporary Buffer for BIG data */ + u_int8_t *bigkey_buf; /* Temporary Buffer for BIG keys */ + u_int16_t *split_buf; /* Temporary buffer for splits */ + CURSOR *seq_cursor; /* Cursor used for hash_seq */ + int32_t errno; /* Error Number -- for DBM compatability */ + int32_t new_file; /* Indicates if fd is backing store or no */ + int32_t save_file; /* Indicates whether we need to flush file at + * exit */ + u_int32_t *mapp[NCACHED];/* Pointers to page maps */ + int32_t nmaps; /* Initial number of bitmaps */ + MPOOL *mp; /* mpool for buffer management */ +} HTAB; + +/* + * Constants + */ +#define MAX_BSIZE 65536 /* 2^16 */ +#define MIN_BUFFERS 6 +#define MINHDRSIZE 512 +#define DEF_CACHESIZE 65536 +#define DEF_BUCKET_SIZE 4096 +#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */ +#define DEF_SEGSIZE 256 +#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */ +#define DEF_DIRSIZE 256 +#define DEF_FFACTOR 65536 +#define MIN_FFACTOR 4 +#define SPLTMAX 8 +#define CHARKEY "%$sniglet^&" +#define NUMKEY 1038583 +#define BYTE_SHIFT 3 +#define INT32_T_TO_BYTE 2 +#define INT32_T_BYTE_SHIFT 5 +#define ALL_SET ((u_int32_t)0xFFFFFFFF) +#define ALL_CLEAR 0 + +#define PTROF(X) ((BUFHEAD *)((ptr_t)(X)&~0x3)) +#define ISMOD(X) ((ptr_t)(X)&0x1) +#define DOMOD(X) ((X) = (int8_t *)((ptr_t)(X)|0x1)) +#define ISDISK(X) ((ptr_t)(X)&0x2) +#define DODISK(X) ((X) = (int8_t *)((ptr_t)(X)|0x2)) + +#define BITS_PER_MAP 32 + +/* Given the address of the beginning of a big map, clear/set the nth bit */ +#define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP))) +#define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP))) +#define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP))) + +/* Overflow management */ +/* + * Overflow page numbers are allocated per split point. At each doubling of + * the table, we can allocate extra pages. So, an overflow page number has + * the top 5 bits indicate which split point and the lower 11 bits indicate + * which page at that split point is indicated (pages within split points are + * numberered starting with 1). + */ + +#define SPLITSHIFT 11 +#define SPLITMASK 0x7FF +#define SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT) +#define OPAGENUM(N) ((N) & SPLITMASK) +#define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) + (O)) + +#define BUCKET_TO_PAGE(B) \ + ((B) + hashp->hdr.hdrpages + ((B) \ + ? hashp->hdr.spares[__log2((B)+1)-1] : 0)) +#define OADDR_TO_PAGE(B) \ + (BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B))) + +#define POW2(N) (1 << (N)) + +#define MAX_PAGES(H) (DB_OFF_T_MAX / (H)->hdr.bsize) + +/* Shorthands for accessing structure */ +#define METADATA_PGNO 0 +#define SPLIT_PGNO 0xFFFF + +typedef struct item_info { + db_pgno_t pgno; + db_pgno_t bucket; + indx_t ndx; + indx_t pgndx; + u_int8_t status; + int32_t seek_size; + db_pgno_t seek_found_page; + indx_t key_off; + indx_t data_off; + u_int8_t caused_expand; +} ITEM_INFO; + + +#define ITEM_ERROR 0 +#define ITEM_OK 1 +#define ITEM_NO_MORE 2 + +#define ITEM_GET_FIRST 0 +#define ITEM_GET_NEXT 1 +#define ITEM_GET_RESET 2 +#define ITEM_GET_DONE 3 +#define ITEM_GET_N 4 + +#define UNKNOWN 0xffffffff /* for num_items */ +#define NO_EXPAND 0xfffffffe diff --git a/src/util/db2/hash/hash_bigkey.c b/src/util/db2/hash/hash_bigkey.c new file mode 100644 index 000000000..689dc3db6 --- /dev/null +++ b/src/util/db2/hash/hash_bigkey.c @@ -0,0 +1,483 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_bigkey.c 8.5 (Berkeley) 11/2/95"; +#endif /* LIBC_SCCS and not lint */ + +/* + * PACKAGE: hash + * DESCRIPTION: + * Big key/data handling for the hashing package. + * + * ROUTINES: + * External + * __big_keydata + * __big_split + * __big_insert + * __big_return + * __big_delete + * __find_last_page + * Internal + * collect_key + * collect_data + */ +#include + +#include +#include + +#ifdef DEBUG +#include +#endif + +#include "db-int.h" +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int32_t collect_key __P((HTAB *, PAGE16 *, int32_t, db_pgno_t *)); +static int32_t collect_data __P((HTAB *, PAGE16 *, int32_t)); + +/* + * Big_insert + * + * You need to do an insert and the key/data pair is greater than + * MINFILL * the bucket size + * + * Returns: + * 0 ==> OK + * -1 ==> ERROR + */ +int32_t +__big_insert(hashp, pagep, key, val) + HTAB *hashp; + PAGE16 *pagep; + const DBT *key, *val; +{ + size_t key_size, val_size; + indx_t key_move_bytes, val_move_bytes; + int8_t *key_data, *val_data, base_page; + + key_data = (int8_t *)key->data; + key_size = key->size; + val_data = (int8_t *)val->data; + val_size = val->size; + + NUM_ENT(pagep) = NUM_ENT(pagep) + 1; + + for (base_page = 1; key_size + val_size;) { + /* Add a page! */ + pagep = + __add_bigpage(hashp, pagep, NUM_ENT(pagep) - 1, base_page); + if (!pagep) + return (-1); + + /* There's just going to be one entry on this page. */ + NUM_ENT(pagep) = 1; + + /* Move the key's data. */ + key_move_bytes = MIN(FREESPACE(pagep), key_size); + /* Mark the page as to how much key & data is on this page. */ + BIGKEYLEN(pagep) = key_move_bytes; + val_move_bytes = + MIN(FREESPACE(pagep) - key_move_bytes, val_size); + BIGDATALEN(pagep) = val_move_bytes; + + /* Note big pages build beginning --> end, not vice versa. */ + if (key_move_bytes) + memmove(BIGKEY(pagep), key_data, key_move_bytes); + if (val_move_bytes) + memmove(BIGDATA(pagep), val_data, val_move_bytes); + + key_size -= key_move_bytes; + key_data += key_move_bytes; + val_size -= val_move_bytes; + val_data += val_move_bytes; + + base_page = 0; + } + __put_page(hashp, pagep, A_RAW, 1); + return (0); +} + +/* + * Called when we need to delete a big pair. + * + * Returns: + * 0 => OK + * -1 => ERROR + */ +int32_t +#ifdef __STDC__ +__big_delete(HTAB *hashp, PAGE16 *pagep, indx_t ndx) +#else +__big_delete(hashp, pagep, ndx) + HTAB *hashp; + PAGE16 *pagep; + u_int32_t ndx; /* Index of big pair on base page. */ +#endif +{ + PAGE16 *last_pagep; + + /* Get first page with big key/data. */ + pagep = __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); + if (!pagep) + return (-1); + + /* + * Traverse through the pages, freeing the previous one (except + * the first) at each new page. + */ + while (NEXT_PGNO(pagep) != INVALID_PGNO) { + last_pagep = pagep; + pagep = __get_page(hashp, NEXT_PGNO(pagep), A_RAW); + if (!pagep) + return (-1); + __delete_page(hashp, last_pagep, A_OVFL); + } + + /* Free the last page in the chain. */ + __delete_page(hashp, pagep, A_OVFL); + return (0); +} + +/* + * Given a key, indicates whether the big key at cursorp matches the + * given key. + * + * Returns: + * 1 = Found! + * 0 = Key not found + * -1 error + */ +int32_t +__find_bigpair(hashp, cursorp, key, size) + HTAB *hashp; + CURSOR *cursorp; + int8_t *key; + int32_t size; +{ + PAGE16 *pagep, *hold_pagep; + db_pgno_t next_pgno; + int32_t ksize; + u_int16_t bytes; + int8_t *kkey; + + ksize = size; + kkey = key; + bytes = 0; + + hold_pagep = NULL; + /* Chances are, hashp->cpage is the base page. */ + if (cursorp->pagep) + pagep = hold_pagep = cursorp->pagep; + else { + pagep = __get_page(hashp, cursorp->pgno, A_RAW); + if (!pagep) + return (-1); + } + + /* + * Now, get the first page with the big stuff on it. + * + * XXX + * KLUDGE: we know that cursor is looking at the _next_ item, so + * we have to look at pgndx - 1. + */ + next_pgno = OADDR_TO_PAGE(DATA_OFF(pagep, (cursorp->pgndx - 1))); + if (!hold_pagep) + __put_page(hashp, pagep, A_RAW, 0); + pagep = __get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + + /* While there are both keys to compare. */ + while ((ksize > 0) && (BIGKEYLEN(pagep))) { + if (ksize < KEY_OFF(pagep, 0) || + memcmp(BIGKEY(pagep), kkey, BIGKEYLEN(pagep))) { + __put_page(hashp, pagep, A_RAW, 0); + return (0); + } + kkey += BIGKEYLEN(pagep); + ksize -= BIGKEYLEN(pagep); + if (NEXT_PGNO(pagep) != INVALID_PGNO) { + next_pgno = NEXT_PGNO(pagep); + __put_page(hashp, pagep, A_RAW, 0); + pagep = __get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + } + } + __put_page(hashp, pagep, A_RAW, 0); +#ifdef DEBUG + assert(ksize >= 0); +#endif + if (ksize != 0) { +#ifdef HASH_STATISTICS + ++hash_collisions; +#endif + return (0); + } else + return (1); +} + +/* + * Fill in the key and data for this big pair. + */ +int32_t +__big_keydata(hashp, pagep, key, val, ndx) + HTAB *hashp; + PAGE16 *pagep; + DBT *key, *val; + int32_t ndx; +{ + ITEM_INFO ii; + PAGE16 *key_pagep; + db_pgno_t last_page; + + key_pagep = + __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); + if (!key_pagep) + return (-1); + key->size = collect_key(hashp, key_pagep, 0, &last_page); + key->data = hashp->bigkey_buf; + __put_page(hashp, key_pagep, A_RAW, 0); + + if (key->size == -1) + return (-1); + + /* Create an item_info to direct __big_return to the beginning pgno. */ + ii.pgno = last_page; + return (__big_return(hashp, &ii, val, 1)); +} + +/* + * Return the big key on page, ndx. + */ +int32_t +#ifdef __STDC__ +__get_bigkey(HTAB *hashp, PAGE16 *pagep, indx_t ndx, DBT *key) +#else +__get_bigkey(hashp, pagep, ndx, key) + HTAB *hashp; + PAGE16 *pagep; + u_int32_t ndx; + DBT *key; +#endif +{ + PAGE16 *key_pagep; + + key_pagep = + __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW); + if (!pagep) + return (-1); + key->size = collect_key(hashp, key_pagep, 0, NULL); + key->data = hashp->bigkey_buf; + + __put_page(hashp, key_pagep, A_RAW, 0); + + return (0); +} + +/* + * Return the big key and data indicated in item_info. + */ +int32_t +__big_return(hashp, item_info, val, on_bigkey_page) + HTAB *hashp; + ITEM_INFO *item_info; + DBT *val; + int32_t on_bigkey_page; +{ + PAGE16 *pagep; + db_pgno_t next_pgno; + + if (!on_bigkey_page) { + /* Get first page with big pair on it. */ + pagep = __get_page(hashp, + OADDR_TO_PAGE(item_info->data_off), A_RAW); + if (!pagep) + return (-1); + } else { + pagep = __get_page(hashp, item_info->pgno, A_RAW); + if (!pagep) + return (-1); + } + + /* Traverse through the bigkey pages until a page with data is found. */ + while (!BIGDATALEN(pagep)) { + next_pgno = NEXT_PGNO(pagep); + __put_page(hashp, pagep, A_RAW, 0); + pagep = __get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + } + + val->size = collect_data(hashp, pagep, 0); + if (val->size < 1) + return (-1); + val->data = (void *)hashp->bigdata_buf; + + __put_page(hashp, pagep, A_RAW, 0); + return (0); +} + +/* + * Given a page with a big key on it, traverse through the pages counting data + * length, and collect all of the data on the way up. Store the key in + * hashp->bigkey_buf. last_page indicates to the calling function what the + * last page with key on it is; this will help if you later want to retrieve + * the data portion. + * + * Does the work for __get_bigkey. + * + * Return total length of data; -1 if error. + */ +static int32_t +collect_key(hashp, pagep, len, last_page) + HTAB *hashp; + PAGE16 *pagep; + int32_t len; + db_pgno_t *last_page; +{ + PAGE16 *next_pagep; + int32_t totlen, retval; + db_pgno_t next_pgno; +#ifdef DEBUG + db_pgno_t save_addr; +#endif + + /* If this is the last page with key. */ + if (BIGDATALEN(pagep)) { + totlen = len + BIGKEYLEN(pagep); + if (hashp->bigkey_buf) + free(hashp->bigkey_buf); + hashp->bigkey_buf = (char *)malloc(totlen); + if (!hashp->bigkey_buf) + return (-1); + memcpy(hashp->bigkey_buf + len, + BIGKEY(pagep), BIGKEYLEN(pagep)); + if (last_page) + *last_page = ADDR(pagep); + return (totlen); + } + + /* Key filled up all of last key page, so we've gone 1 too far. */ + if (BIGKEYLEN(pagep) == 0) { + if (hashp->bigkey_buf) + free(hashp->bigkey_buf); + hashp->bigkey_buf = (char *)malloc(len); + return (hashp->bigkey_buf ? len : -1); + } + totlen = len + BIGKEYLEN(pagep); + + /* Set pagep to the next page in the chain. */ + if (last_page) + *last_page = ADDR(pagep); + next_pgno = NEXT_PGNO(pagep); + next_pagep = __get_page(hashp, next_pgno, A_RAW); + if (!next_pagep) + return (-1); +#ifdef DEBUG + save_addr = ADDR(pagep); +#endif + retval = collect_key(hashp, next_pagep, totlen, last_page); + +#ifdef DEBUG + assert(save_addr == ADDR(pagep)); +#endif + memcpy(hashp->bigkey_buf + len, BIGKEY(pagep), BIGKEYLEN(pagep)); + __put_page(hashp, next_pagep, A_RAW, 0); + + return (retval); +} + +/* + * Given a page with big data on it, recur through the pages counting data + * length, and collect all of the data on the way up. Store the data in + * hashp->bigdata_buf. + * + * Does the work for __big_return. + * + * Return total length of data; -1 if error. + */ +static int32_t +collect_data(hashp, pagep, len) + HTAB *hashp; + PAGE16 *pagep; + int32_t len; +{ + PAGE16 *next_pagep; + int32_t totlen, retval; + db_pgno_t next_pgno; +#ifdef DEBUG + db_pgno_t save_addr; +#endif + + /* If there is no next page. */ + if (NEXT_PGNO(pagep) == INVALID_PGNO) { + if (hashp->bigdata_buf) + free(hashp->bigdata_buf); + totlen = len + BIGDATALEN(pagep); + hashp->bigdata_buf = (char *)malloc(totlen); + if (!hashp->bigdata_buf) + return (-1); + memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep), + BIGDATA(pagep), BIGDATALEN(pagep)); + return (totlen); + } + totlen = len + BIGDATALEN(pagep); + + /* Set pagep to the next page in the chain. */ + next_pgno = NEXT_PGNO(pagep); + next_pagep = __get_page(hashp, next_pgno, A_RAW); + if (!next_pagep) + return (-1); + +#ifdef DEBUG + save_addr = ADDR(pagep); +#endif + retval = collect_data(hashp, next_pagep, totlen); +#ifdef DEBUG + assert(save_addr == ADDR(pagep)); +#endif + memcpy(hashp->bigdata_buf + totlen - BIGDATALEN(pagep), + BIGDATA(pagep), BIGDATALEN(pagep)); + __put_page(hashp, next_pagep, A_RAW, 0); + + return (retval); +} diff --git a/src/util/db2/hash/hash_debug.c b/src/util/db2/hash/hash_debug.c new file mode 100644 index 000000000..ed99c6932 --- /dev/null +++ b/src/util/db2/hash/hash_debug.c @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1995 + * The President and Fellows of Harvard University + * + * This code is derived from software contributed to Harvard by + * Jeremy Rassen. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_debug.c 8.4 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +#ifdef DEBUG +/* + * PACKAGE: hashing + * + * DESCRIPTION: + * Debug routines. + * + * ROUTINES: + * + * External + * __dump_bucket + */ +#include + +#include "db-int.h" +#include "hash.h" +#include "page.h" +#include "extern.h" +#include "compat.h" + +void +__dump_bucket(hashp, bucket) + HTAB *hashp; + u_int32_t bucket; +{ + CURSOR cursor; + DBT key, val; + ITEM_INFO item_info; + int var; + char *cp; + + cursor.pagep = NULL; + item_info.seek_size = 0; + item_info.seek_found_page = 0; + + __get_item_reset(hashp, &cursor); + + cursor.bucket = bucket; + for (;;) { + __get_item_next(hashp, &cursor, &key, &val, &item_info); + if (item_info.status == ITEM_ERROR) { + (void)printf("get_item_next returned error\n"); + break; + } else if (item_info.status == ITEM_NO_MORE) + break; + + if (item_info.key_off == BIGPAIR) { + if (__big_keydata(hashp, cursor.pagep, &key, &val, + item_info.pgndx)) { + (void)printf("__big_keydata returned error\n"); + break; + } + } + + if (key.size == sizeof(int)) { + memcpy(&var, key.data, sizeof(int)); + (void)printf("%d\n", var); + } else { + for (cp = (char *)key.data; key.size--; cp++) + (void)printf("%c", *cp); + (void)printf("\n"); + } + } + __get_item_done(hashp, &cursor); +} +#endif /* DEBUG */ diff --git a/src/util/db2/hash/hash_func.c b/src/util/db2/hash/hash_func.c new file mode 100644 index 000000000..efa2a2843 --- /dev/null +++ b/src/util/db2/hash/hash_func.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_func.c 8.4 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include "db-int.h" +#include "hash.h" +#include "page.h" +#include "extern.h" + +static u_int32_t hash1 __P((const void *, size_t)); +static u_int32_t hash2 __P((const void *, size_t)); +static u_int32_t hash3 __P((const void *, size_t)); +static u_int32_t hash4 __P((const void *, size_t)); + +/* Default hash function. */ +u_int32_t (*__default_hash) __P((const void *, size_t)) = hash4; + +/* + * Assume that we've already split the bucket to which this key hashes, + * calculate that bucket, and check that in fact we did already split it. + * + * EJB's original hsearch hash. + */ +#define PRIME1 37 +#define PRIME2 1048583 + +u_int32_t +hash1(key, len) + const void *key; + size_t len; +{ + u_int32_t h; + u_int8_t *k; + + h = 0; + k = (u_int8_t *)key; + /* Convert string to integer */ + while (len--) + h = h * PRIME1 ^ (*k++ - ' '); + h %= PRIME2; + return (h); +} + +/* + * Phong Vo's linear congruential hash + */ +#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c)) + +u_int32_t +hash2(key, len) + const void *key; + size_t len; +{ + u_int32_t h; + u_int8_t *e, c, *k; + + k = (u_int8_t *)key; + e = k + len; + for (h = 0; k != e;) { + c = *k++; + if (!c && k > e) + break; + dcharhash(h, c); + } + return (h); +} + +/* + * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte + * units. On the first time through the loop we get the "leftover bytes" + * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle + * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If + * this routine is heavily used enough, it's worth the ugly coding. + * + * Ozan Yigit's original sdbm hash. + */ +u_int32_t +hash3(key, len) + const void *key; + size_t len; +{ + u_int32_t n, loop; + u_int8_t *k; + +#define HASHC n = *k++ + 65599 * n + + n = 0; + k = (u_int8_t *)key; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } while (--loop); + } + + } + return (n); +} + +/* Chris Torek's hash function. */ +u_int32_t +hash4(key, len) + const void *key; + size_t len; +{ + u_int32_t h, loop; + u_int8_t *k; + +#define HASH4a h = (h << 5) - h + *k++; +#define HASH4b h = (h << 5) + h + *k++; +#define HASH4 HASH4b + + h = 0; + k = (u_int8_t *)key; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASH4; + case 7: + HASH4; + case 6: + HASH4; + case 5: + HASH4; + case 4: + HASH4; + case 3: + HASH4; + case 2: + HASH4; + case 1: + HASH4; + } while (--loop); + } + + } + return (h); +} diff --git a/src/util/db2/hash/hash_log2.c b/src/util/db2/hash/hash_log2.c new file mode 100644 index 000000000..8604945e8 --- /dev/null +++ b/src/util/db2/hash/hash_log2.c @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_log2.c 8.4 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +#include "db-int.h" + +u_int32_t +__log2(num) + u_int32_t num; +{ + u_int32_t i, limit; + + limit = 1; + for (i = 0; limit < num; limit = limit << 1, i++); + return (i); +} diff --git a/src/util/db2/hash/hash_page.c b/src/util/db2/hash/hash_page.c new file mode 100644 index 000000000..8622075d1 --- /dev/null +++ b/src/util/db2/hash/hash_page.c @@ -0,0 +1,1380 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hash_page.c 8.11 (Berkeley) 11/7/95"; +#endif /* LIBC_SCCS and not lint */ + +/* + * PACKAGE: hashing + * + * DESCRIPTION: + * Page manipulation for hashing package. + * + * ROUTINES: + * + * External + * __get_page + * __add_ovflpage + * Internal + * overflow_page + * open_temp + */ + +#include + +#ifdef DEBUG +#include +#endif +#include +#include +#include +#include + +#include "db-int.h" +#include "hash.h" +#include "page.h" +#include "extern.h" + +static int32_t add_bigptr __P((HTAB *, ITEM_INFO *, indx_t)); +static u_int32_t *fetch_bitmap __P((HTAB *, int32_t)); +static u_int32_t first_free __P((u_int32_t)); +static indx_t next_realkey __P((PAGE16 *, indx_t)); +static u_int16_t overflow_page __P((HTAB *)); +static void page_init __P((HTAB *, PAGE16 *, db_pgno_t, u_int8_t)); +static indx_t prev_realkey __P((PAGE16 *, indx_t)); +static void putpair __P((PAGE8 *, const DBT *, const DBT *)); +static void swap_page_header_in __P((PAGE16 *)); +static void swap_page_header_out __P((PAGE16 *)); + +#ifdef DEBUG_SLOW +static void account_page(HTAB *, db_pgno_t, int); +#endif + +u_int32_t +__get_item(hashp, cursorp, key, val, item_info) + HTAB *hashp; + CURSOR *cursorp; + DBT *key, *val; + ITEM_INFO *item_info; +{ + db_pgno_t next_pgno; + int32_t i; + + /* Check if we need to get a page. */ + if (!cursorp->pagep) { + if (cursorp->pgno == INVALID_PGNO) { + cursorp->pagep = + __get_page(hashp, cursorp->bucket, A_BUCKET); + cursorp->pgno = ADDR(cursorp->pagep); + cursorp->ndx = 0; + cursorp->pgndx = 0; + } else + cursorp->pagep = + __get_page(hashp, cursorp->pgno, A_RAW); + if (!cursorp->pagep) { + item_info->status = ITEM_ERROR; + return (-1); + } + } + if (item_info->seek_size && + FREESPACE(cursorp->pagep) > item_info->seek_size) + item_info->seek_found_page = cursorp->pgno; + + if (cursorp->pgndx == NUM_ENT(cursorp->pagep)) { + /* Fetch next page. */ + if (NEXT_PGNO(cursorp->pagep) == INVALID_PGNO) { + item_info->status = ITEM_NO_MORE; + return (-1); + } + next_pgno = NEXT_PGNO(cursorp->pagep); + cursorp->pgndx = 0; + __put_page(hashp, cursorp->pagep, A_RAW, 0); + cursorp->pagep = __get_page(hashp, next_pgno, A_RAW); + if (!cursorp->pagep) { + item_info->status = ITEM_ERROR; + return (-1); + } + cursorp->pgno = next_pgno; + } + if (KEY_OFF(cursorp->pagep, cursorp->pgndx) != BIGPAIR) { + if ((i = prev_realkey(cursorp->pagep, cursorp->pgndx)) == + cursorp->pgndx) + key->size = hashp->hdr.bsize - + KEY_OFF(cursorp->pagep, cursorp->pgndx); + else + key->size = DATA_OFF(cursorp->pagep, i) - + KEY_OFF(cursorp->pagep, cursorp->pgndx); + } + + /* + * All of this information will be set incorrectly for big keys, but + * it will be ignored anyway. + */ + val->size = KEY_OFF(cursorp->pagep, cursorp->pgndx) - + DATA_OFF(cursorp->pagep, cursorp->pgndx); + key->data = KEY(cursorp->pagep, cursorp->pgndx); + val->data = DATA(cursorp->pagep, cursorp->pgndx); + item_info->pgno = cursorp->pgno; + item_info->bucket = cursorp->bucket; + item_info->ndx = cursorp->ndx; + item_info->pgndx = cursorp->pgndx; + item_info->key_off = KEY_OFF(cursorp->pagep, cursorp->pgndx); + item_info->data_off = DATA_OFF(cursorp->pagep, cursorp->pgndx); + item_info->status = ITEM_OK; + + return (0); +} + +u_int32_t +__get_item_reset(hashp, cursorp) + HTAB *hashp; + CURSOR *cursorp; +{ + if (cursorp->pagep) + __put_page(hashp, cursorp->pagep, A_RAW, 0); + cursorp->pagep = NULL; + cursorp->bucket = -1; + cursorp->ndx = 0; + cursorp->pgndx = 0; + cursorp->pgno = INVALID_PGNO; + return (0); +} + +u_int32_t +__get_item_done(hashp, cursorp) + HTAB *hashp; + CURSOR *cursorp; +{ + if (cursorp->pagep) + __put_page(hashp, cursorp->pagep, A_RAW, 0); + cursorp->pagep = NULL; + + /* + * We don't throw out the page number since we might want to + * continue getting on this page. + */ + return (0); +} + +u_int32_t +__get_item_first(hashp, cursorp, key, val, item_info) + HTAB *hashp; + CURSOR *cursorp; + DBT *key, *val; + ITEM_INFO *item_info; +{ + __get_item_reset(hashp, cursorp); + cursorp->bucket = 0; + return (__get_item_next(hashp, cursorp, key, val, item_info)); +} + +/* + * Returns a pointer to key/data pair on a page. In the case of bigkeys, + * just returns the page number and index of the bigkey pointer pair. + */ +u_int32_t +__get_item_next(hashp, cursorp, key, val, item_info) + HTAB *hashp; + CURSOR *cursorp; + DBT *key, *val; + ITEM_INFO *item_info; +{ + int stat; + + stat = __get_item(hashp, cursorp, key, val, item_info); + cursorp->ndx++; + cursorp->pgndx++; + return (stat); +} + +/* + * Put a non-big pair on a page. + */ +static void +putpair(p, key, val) + PAGE8 *p; + const DBT *key, *val; +{ + u_int16_t *pagep, n, off; + + pagep = (PAGE16 *)p; + + /* Items on the page are 0-indexed. */ + n = NUM_ENT(pagep); + off = OFFSET(pagep) - key->size + 1; + memmove(p + off, key->data, key->size); + KEY_OFF(pagep, n) = off; + + off -= val->size; + memmove(p + off, val->data, val->size); + DATA_OFF(pagep, n) = off; + + /* Adjust page info. */ + NUM_ENT(pagep) = n + 1; + OFFSET(pagep) = off - 1; +} + +/* + * Returns the index of the next non-bigkey pair after n on the page. + * Returns -1 if there are no more non-big things on the page. + */ +indx_t +#ifdef __STDC__ +next_realkey(PAGE16 * pagep, indx_t n) +#else +next_realkey(pagep, n) + PAGE16 *pagep; + u_int32_t n; +#endif +{ + indx_t i; + + for (i = n + 1; i < NUM_ENT(pagep); i++) + if (KEY_OFF(pagep, i) != BIGPAIR) + return (i); + return (-1); +} + +/* + * Returns the index of the previous non-bigkey pair after n on the page. + * Returns n if there are no previous non-big things on the page. + */ +static indx_t +#ifdef __STDC__ +prev_realkey(PAGE16 * pagep, indx_t n) +#else +prev_realkey(pagep, n) + PAGE16 *pagep; + u_int32_t n; +#endif +{ + int32_t i; + + /* Need a signed value to do the compare properly. */ + for (i = n - 1; i > -1; i--) + if (KEY_OFF(pagep, i) != BIGPAIR) + return (i); + return (n); +} + +/* + * Returns: + * 0 OK + * -1 error + */ +extern int32_t +__delpair(hashp, cursorp, item_info) + HTAB *hashp; + CURSOR *cursorp; + ITEM_INFO *item_info; +{ + PAGE16 *pagep; + indx_t ndx; + short check_ndx; + int16_t delta, len, next_key; + int32_t n; + u_int8_t *src, *dest; + + ndx = cursorp->pgndx; + if (!cursorp->pagep) { + pagep = __get_page(hashp, cursorp->pgno, A_RAW); + if (!pagep) + return (-1); + /* + * KLUGE: pgndx has gone one too far, because cursor points + * to the _next_ item. Use pgndx - 1. + */ + --ndx; + } else + pagep = cursorp->pagep; +#ifdef DEBUG + assert(ADDR(pagep) == cursorp->pgno); +#endif + + if (KEY_OFF(pagep, ndx) == BIGPAIR) { + delta = 0; + __big_delete(hashp, pagep, ndx); + } else { + /* + * Compute "delta", the amount we have to shift all of the + * offsets. To find the delta, we need to make sure that + * we aren't looking at the DATA_OFF of a big/keydata pair. + */ + for (check_ndx = (short)(ndx - 1); + check_ndx >= 0 && KEY_OFF(pagep, check_ndx) == BIGPAIR; + check_ndx--); + if (check_ndx < 0) + delta = hashp->hdr.bsize - DATA_OFF(pagep, ndx); + else + delta = + DATA_OFF(pagep, check_ndx) - DATA_OFF(pagep, ndx); + + /* + * The hard case: we want to remove something other than + * the last item on the page. We need to shift data and + * offsets down. + */ + if (ndx != NUM_ENT(pagep) - 1) { + /* + * Move the data: src is the address of the last data + * item on the page. + */ + src = (u_int8_t *)pagep + OFFSET(pagep) + 1; + /* + * Length is the distance between where to start + * deleting and end of the data on the page. + */ + len = DATA_OFF(pagep, ndx) - (OFFSET(pagep) + 1); + /* + * Dest is the location of the to-be-deleted item + * occupied - length. + */ + if (check_ndx < 0) + dest = + (u_int8_t *)pagep + hashp->hdr.bsize - len; + else + dest = (u_int8_t *)pagep + + DATA_OFF(pagep, (check_ndx)) - len; + memmove(dest, src, len); + } + } + + /* Adjust the offsets. */ + for (n = ndx; n < NUM_ENT(pagep) - 1; n++) + if (KEY_OFF(pagep, (n + 1)) != BIGPAIR) { + next_key = next_realkey(pagep, n); +#ifdef DEBUG + assert(next_key != -1); +#endif + KEY_OFF(pagep, n) = KEY_OFF(pagep, (n + 1)) + delta; + DATA_OFF(pagep, n) = DATA_OFF(pagep, (n + 1)) + delta; + } else { + KEY_OFF(pagep, n) = KEY_OFF(pagep, (n + 1)); + DATA_OFF(pagep, n) = DATA_OFF(pagep, (n + 1)); + } + + /* Adjust page metadata. */ + OFFSET(pagep) = OFFSET(pagep) + delta; + NUM_ENT(pagep) = NUM_ENT(pagep) - 1; + + --hashp->hdr.nkeys; + + /* Is this page now an empty overflow page? If so, free it. */ + if (TYPE(pagep) == HASH_OVFLPAGE && NUM_ENT(pagep) == 0) { + PAGE16 *empty_page; + db_pgno_t to_find, next_pgno, link_page; + + /* + * We need to go back to the first page in the chain and + * look for this page so that we can update the previous + * page's NEXT_PGNO field. + */ + to_find = ADDR(pagep); + empty_page = pagep; + link_page = NEXT_PGNO(empty_page); + pagep = __get_page(hashp, item_info->bucket, A_BUCKET); + if (!pagep) + return (-1); + while (NEXT_PGNO(pagep) != to_find) { + next_pgno = NEXT_PGNO(pagep); +#ifdef DEBUG + assert(next_pgno != INVALID_PGNO); +#endif + __put_page(hashp, pagep, A_RAW, 0); + pagep = __get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + } + + /* + * At this point, pagep should point to the page before the + * page to be deleted. + */ + NEXT_PGNO(pagep) = link_page; + if (item_info->pgno == to_find) { + item_info->pgno = ADDR(pagep); + item_info->pgndx = NUM_ENT(pagep); + item_info->seek_found_page = ADDR(pagep); + } + __delete_page(hashp, empty_page, A_OVFL); + } + __put_page(hashp, pagep, A_RAW, 1); + + return (0); +} + +extern int32_t +__split_page(hashp, obucket, nbucket) + HTAB *hashp; + u_int32_t obucket, nbucket; +{ + DBT key, val; + ITEM_INFO old_ii, new_ii; + PAGE16 *old_pagep, *temp_pagep; + db_pgno_t next_pgno; + int32_t off; + u_int16_t n; + int8_t base_page; + + off = hashp->hdr.bsize; + old_pagep = __get_page(hashp, obucket, A_BUCKET); + + base_page = 1; + + temp_pagep = hashp->split_buf; + memcpy(temp_pagep, old_pagep, hashp->hdr.bsize); + + page_init(hashp, old_pagep, ADDR(old_pagep), HASH_PAGE); + __put_page(hashp, old_pagep, A_RAW, 1); + + old_ii.pgno = BUCKET_TO_PAGE(obucket); + new_ii.pgno = BUCKET_TO_PAGE(nbucket); + old_ii.bucket = obucket; + new_ii.bucket = nbucket; + old_ii.seek_found_page = new_ii.seek_found_page = 0; + + while (temp_pagep != 0) { + off = hashp->hdr.bsize; + for (n = 0; n < NUM_ENT(temp_pagep); n++) { + if (KEY_OFF(temp_pagep, n) == BIGPAIR) { + __get_bigkey(hashp, temp_pagep, n, &key); + if (__call_hash(hashp, + key.data, key.size) == obucket) + add_bigptr(hashp, &old_ii, + DATA_OFF(temp_pagep, n)); + else + add_bigptr(hashp, &new_ii, + DATA_OFF(temp_pagep, n)); + } else { + key.size = off - KEY_OFF(temp_pagep, n); + key.data = KEY(temp_pagep, n); + off = KEY_OFF(temp_pagep, n); + val.size = off - DATA_OFF(temp_pagep, n); + val.data = DATA(temp_pagep, n); + if (__call_hash(hashp, + key.data, key.size) == obucket) + __addel(hashp, &old_ii, &key, &val, + NO_EXPAND, 1); + else + __addel(hashp, &new_ii, &key, &val, + NO_EXPAND, 1); + off = DATA_OFF(temp_pagep, n); + } + } + next_pgno = NEXT_PGNO(temp_pagep); + + /* Clear temp_page; if it's an overflow page, free it. */ + if (!base_page) + __delete_page(hashp, temp_pagep, A_OVFL); + else + base_page = 0; + if (next_pgno != INVALID_PGNO) + temp_pagep = __get_page(hashp, next_pgno, A_RAW); + else + break; + } + return (0); +} + +/* + * Add the given pair to the page. + * + * + * Returns: + * 0 ==> OK + * -1 ==> failure + */ +extern int32_t +#ifdef __STDC__ +__addel(HTAB *hashp, ITEM_INFO *item_info, const DBT *key, const DBT *val, + u_int32_t num_items, const u_int8_t expanding) +#else +__addel(hashp, item_info, key, val, num_items, expanding) + HTAB *hashp; + ITEM_INFO *item_info; + const DBT *key, *val; + u_int32_t num_items; + const u_int32_t expanding; +#endif +{ + PAGE16 *pagep; + int32_t do_expand; + db_pgno_t next_pgno; + + do_expand = 0; + + pagep = __get_page(hashp, + item_info->seek_found_page != 0 ? + item_info->seek_found_page : item_info->pgno, A_RAW); + if (!pagep) + return (-1); + + /* Advance to first page in chain with room for item. */ + while (NUM_ENT(pagep) && NEXT_PGNO(pagep) != INVALID_PGNO) { + /* + * This may not be the end of the chain, but the pair may fit + * anyway. + */ + if (ISBIG(PAIRSIZE(key, val), hashp) && BIGPAIRFITS(pagep)) + break; + if (PAIRFITS(pagep, key, val)) + break; + next_pgno = NEXT_PGNO(pagep); + __put_page(hashp, pagep, A_RAW, 0); + pagep = (PAGE16 *)__get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + } + + if ((ISBIG(PAIRSIZE(key, val), hashp) && + !BIGPAIRFITS(pagep)) || + (!ISBIG(PAIRSIZE(key, val), hashp) && + !PAIRFITS(pagep, key, val))) { + do_expand = 1; + pagep = __add_ovflpage(hashp, pagep); + if (!pagep) + return (-1); + + if ((ISBIG(PAIRSIZE(key, val), hashp) && + !BIGPAIRFITS(pagep)) || + (!ISBIG(PAIRSIZE(key, val), hashp) && + !PAIRFITS(pagep, key, val))) { + __put_page(hashp, pagep, A_RAW, 0); + return (-1); + } + } + + /* At this point, we know the page fits, so we just add it */ + + if (ISBIG(PAIRSIZE(key, val), hashp)) { + if (__big_insert(hashp, pagep, key, val)) + return (-1); + } else { + putpair((PAGE8 *)pagep, key, val); + } + + /* + * For splits, we are going to update item_info's page number + * field, so that we can easily return to the same page the + * next time we come in here. For other operations, this shouldn't + * matter, since adds are the last thing that happens before we + * return to the user program. + */ + item_info->pgno = ADDR(pagep); + + if (!expanding) + hashp->hdr.nkeys++; + + /* Kludge: if this is a big page, then it's already been put. */ + if (!ISBIG(PAIRSIZE(key, val), hashp)) + __put_page(hashp, pagep, A_RAW, 1); + + if (expanding) + item_info->caused_expand = 0; + else + switch (num_items) { + case NO_EXPAND: + item_info->caused_expand = 0; + break; + case UNKNOWN: + item_info->caused_expand |= + (hashp->hdr.nkeys / hashp->hdr.max_bucket) > + hashp->hdr.ffactor || + item_info->pgndx > hashp->hdr.ffactor; + break; + default: + item_info->caused_expand = + num_items > hashp->hdr.ffactor ? 1 : do_expand; + break; + } + return (0); +} + +/* + * Special __addel used in big splitting; this one just puts the pointer + * to an already-allocated big page in the appropriate bucket. + */ +int32_t +#ifdef __STDC__ +add_bigptr(HTAB * hashp, ITEM_INFO * item_info, indx_t big_pgno) +#else +add_bigptr(hashp, item_info, big_pgno) + HTAB *hashp; + ITEM_INFO *item_info; + u_int32_t big_pgno; +#endif +{ + PAGE16 *pagep; + db_pgno_t next_pgno; + + pagep = __get_page(hashp, item_info->bucket, A_BUCKET); + if (!pagep) + return (-1); + + /* + * Note: in __addel(), we used item_info->pgno for the beginning of + * our search for space. Now, we use item_info->bucket, since we + * know that the space required by a big pair on the base page is + * quite small, and we may very well find that space early in the + * chain. + */ + + /* Find first page in chain that has space for a big pair. */ + while (NUM_ENT(pagep) && (NEXT_PGNO(pagep) != INVALID_PGNO)) { + if (BIGPAIRFITS(pagep)) + break; + next_pgno = NEXT_PGNO(pagep); + __put_page(hashp, pagep, A_RAW, 0); + pagep = __get_page(hashp, next_pgno, A_RAW); + if (!pagep) + return (-1); + } + if (!BIGPAIRFITS(pagep)) { + pagep = __add_ovflpage(hashp, pagep); + if (!pagep) + return (-1); +#ifdef DEBUG + assert(BIGPAIRFITS(pagep)); +#endif + } + KEY_OFF(pagep, NUM_ENT(pagep)) = BIGPAIR; + DATA_OFF(pagep, NUM_ENT(pagep)) = big_pgno; + NUM_ENT(pagep) = NUM_ENT(pagep) + 1; + + __put_page(hashp, pagep, A_RAW, 1); + + return (0); +} + +/* + * + * Returns: + * pointer on success + * NULL on error + */ +extern PAGE16 * +__add_ovflpage(hashp, pagep) + HTAB *hashp; + PAGE16 *pagep; +{ + PAGE16 *new_pagep; + u_int16_t ovfl_num; + + /* Check if we are dynamically determining the fill factor. */ + if (hashp->hdr.ffactor == DEF_FFACTOR) { + hashp->hdr.ffactor = NUM_ENT(pagep) >> 1; + if (hashp->hdr.ffactor < MIN_FFACTOR) + hashp->hdr.ffactor = MIN_FFACTOR; + } + ovfl_num = overflow_page(hashp); + + if (__new_page(hashp, (u_int32_t)ovfl_num, A_OVFL) != 0) + return (NULL); + + if (!ovfl_num || !(new_pagep = __get_page(hashp, ovfl_num, A_OVFL))) + return (NULL); + + NEXT_PGNO(pagep) = (db_pgno_t)OADDR_TO_PAGE(ovfl_num); + TYPE(new_pagep) = HASH_OVFLPAGE; + + __put_page(hashp, pagep, A_RAW, 1); + +#ifdef HASH_STATISTICS + hash_overflows++; +#endif + return (new_pagep); +} + +/* + * + * Returns: + * pointer on success + * NULL on error + */ +extern PAGE16 * +#ifdef __STDC__ +__add_bigpage(HTAB * hashp, PAGE16 * pagep, indx_t ndx, const u_int8_t + is_basepage) +#else +__add_bigpage(hashp, pagep, ndx, is_basepage) + HTAB *hashp; + PAGE16 *pagep; + u_int32_t ndx; + const u_int32_t is_basepage; +#endif +{ + PAGE16 *new_pagep; + u_int16_t ovfl_num; + + ovfl_num = overflow_page(hashp); + + if (__new_page(hashp, (u_int32_t)ovfl_num, A_OVFL) != 0) + return (NULL); + + if (!ovfl_num || !(new_pagep = __get_page(hashp, ovfl_num, A_OVFL))) + return (NULL); + + if (is_basepage) { + KEY_OFF(pagep, ndx) = BIGPAIR; + DATA_OFF(pagep, ndx) = (indx_t)ovfl_num; + } else + NEXT_PGNO(pagep) = ADDR(new_pagep); + + __put_page(hashp, pagep, A_RAW, 1); + + TYPE(new_pagep) = HASH_BIGPAGE; + +#ifdef HASH_STATISTICS + hash_bigpages++; +#endif + return (new_pagep); +} + +static void +#ifdef __STDC__ +page_init(HTAB * hashp, PAGE16 * pagep, db_pgno_t pgno, u_int8_t type) +#else +page_init(hashp, pagep, pgno, type) + HTAB *hashp; + PAGE16 *pagep; + db_pgno_t pgno; + u_int32_t type; +#endif +{ + NUM_ENT(pagep) = 0; + PREV_PGNO(pagep) = NEXT_PGNO(pagep) = INVALID_PGNO; + TYPE(pagep) = type; + OFFSET(pagep) = hashp->hdr.bsize - 1; + /* + * Note: since in the current version ADDR(pagep) == PREV_PGNO(pagep), + * make sure that ADDR(pagep) is set after resetting PREV_PGNO(pagep). + * We reset PREV_PGNO(pagep) just in case the macros are changed. + */ + ADDR(pagep) = pgno; + + return; +} + +int32_t +__new_page(hashp, addr, addr_type) + HTAB *hashp; + u_int32_t addr; + int32_t addr_type; +{ + db_pgno_t paddr; + PAGE16 *pagep; + + switch (addr_type) { /* Convert page number. */ + case A_BUCKET: + paddr = BUCKET_TO_PAGE(addr); + break; + case A_OVFL: + case A_BITMAP: + paddr = OADDR_TO_PAGE(addr); + break; + default: + paddr = addr; + break; + } + pagep = mpool_new(hashp->mp, &paddr, MPOOL_PAGE_REQUEST); + if (!pagep) + return (-1); +#if DEBUG_SLOW + account_page(hashp, paddr, 1); +#endif + + if (addr_type != A_BITMAP) + page_init(hashp, pagep, paddr, HASH_PAGE); + + __put_page(hashp, pagep, addr_type, 1); + + return (0); +} + +int32_t +__delete_page(hashp, pagep, page_type) + HTAB *hashp; + PAGE16 *pagep; + int32_t page_type; +{ + if (page_type == A_OVFL) + __free_ovflpage(hashp, pagep); + return (mpool_delete(hashp->mp, pagep)); +} + +u_int8_t +is_bitmap_pgno(hashp, pgno) + HTAB *hashp; + db_pgno_t pgno; +{ + int32_t i; + + for (i = 0; i < hashp->nmaps; i++) + if (OADDR_TO_PAGE(hashp->hdr.bitmaps[i]) == pgno) + return (1); + return (0); +} + +void +__pgin_routine(pg_cookie, pgno, page) + void *pg_cookie; + db_pgno_t pgno; + void *page; +{ + HTAB *hashp; + PAGE16 *pagep; + int32_t max, i; + + pagep = (PAGE16 *)page; + hashp = (HTAB *)pg_cookie; + + /* + * There are the following cases for swapping: + * 0) New page that may be unitialized. + * 1) Bucket page or overflow page. Either swap + * the header or initialize the page. + * 2) Bitmap page. Swap the whole page! + * 3) Header pages. Not handled here; these are written directly + * to the file. + */ + + if (NUM_ENT(pagep) == 0 && NEXT_PGNO(pagep) == 0 && + !is_bitmap_pgno(hashp, pgno)) { + /* XXX check for !0 LSN */ + page_init(hashp, pagep, pgno, HASH_PAGE); + return; + } + + if (hashp->hdr.lorder == DB_BYTE_ORDER) + return; + if (is_bitmap_pgno(hashp, pgno)) { + max = hashp->hdr.bsize >> 2; /* divide by 4 bytes */ + for (i = 0; i < max; i++) + M_32_SWAP(((int32_t *)pagep)[i]); + } else + swap_page_header_in(pagep); +} + +void +__pgout_routine(pg_cookie, pgno, page) + void *pg_cookie; + db_pgno_t pgno; + void *page; +{ + HTAB *hashp; + PAGE16 *pagep; + int32_t i, max; + + pagep = (PAGE16 *)page; + hashp = (HTAB *)pg_cookie; + + /* + * There are the following cases for swapping: + * 1) Bucket page or overflow page. Just swap the header. + * 2) Bitmap page. Swap the whole page! + * 3) Header pages. Not handled here; these are written directly + * to the file. + */ + + if (hashp->hdr.lorder == DB_BYTE_ORDER) + return; + if (is_bitmap_pgno(hashp, pgno)) { + max = hashp->hdr.bsize >> 2; /* divide by 4 bytes */ + for (i = 0; i < max; i++) + M_32_SWAP(((int32_t *)pagep)[i]); + } else + swap_page_header_out(pagep); +} + +/* + * + * Returns: + * 0 ==> OK + * -1 ==>failure + */ +extern int32_t +__put_page(hashp, pagep, addr_type, is_dirty) + HTAB *hashp; + PAGE16 *pagep; + int32_t addr_type, is_dirty; +{ +#if DEBUG_SLOW + account_page(hashp, + ((BKT *)((char *)pagep - sizeof(BKT)))->pgno, -1); +#endif + + return (mpool_put(hashp->mp, pagep, (is_dirty ? MPOOL_DIRTY : 0))); +} + +/* + * Returns: + * 0 indicates SUCCESS + * -1 indicates FAILURE + */ +extern PAGE16 * +__get_page(hashp, addr, addr_type) + HTAB *hashp; + u_int32_t addr; + int32_t addr_type; +{ + PAGE16 *pagep; + db_pgno_t paddr; + + switch (addr_type) { /* Convert page number. */ + case A_BUCKET: + paddr = BUCKET_TO_PAGE(addr); + break; + case A_OVFL: + case A_BITMAP: + paddr = OADDR_TO_PAGE(addr); + break; + default: + paddr = addr; + break; + } + pagep = (PAGE16 *)mpool_get(hashp->mp, paddr, 0); + +#if DEBUG_SLOW + account_page(hashp, paddr, 1); +#endif +#ifdef DEBUG + assert(ADDR(pagep) == paddr || ADDR(pagep) == 0 || + addr_type == A_BITMAP || addr_type == A_HEADER); +#endif + + return (pagep); +} + +void +swap_page_header_in(pagep) + PAGE16 *pagep; +{ + u_int32_t i; + + /* can leave type and filler alone, since they're 1-byte quantities */ + + M_32_SWAP(PREV_PGNO(pagep)); + M_32_SWAP(NEXT_PGNO(pagep)); + M_16_SWAP(NUM_ENT(pagep)); + M_16_SWAP(OFFSET(pagep)); + + for (i = 0; i < NUM_ENT(pagep); i++) { + M_16_SWAP(KEY_OFF(pagep, i)); + M_16_SWAP(DATA_OFF(pagep, i)); + } +} + +void +swap_page_header_out(pagep) + PAGE16 *pagep; +{ + u_int32_t i; + + for (i = 0; i < NUM_ENT(pagep); i++) { + M_16_SWAP(KEY_OFF(pagep, i)); + M_16_SWAP(DATA_OFF(pagep, i)) + } + + /* can leave type and filler alone, since they're 1-byte quantities */ + + M_32_SWAP(PREV_PGNO(pagep)); + M_32_SWAP(NEXT_PGNO(pagep)); + M_16_SWAP(NUM_ENT(pagep)); + M_16_SWAP(OFFSET(pagep)); +} + +#define BYTE_MASK ((1 << INT32_T_BYTE_SHIFT) -1) +/* + * Initialize a new bitmap page. Bitmap pages are left in memory + * once they are read in. + */ +extern int32_t +__ibitmap(hashp, pnum, nbits, ndx) + HTAB *hashp; + int32_t pnum, nbits, ndx; +{ + u_int32_t *ip; + int32_t clearbytes, clearints; + + /* make a new bitmap page */ + if (__new_page(hashp, pnum, A_BITMAP) != 0) + return (1); + if (!(ip = (u_int32_t *)__get_page(hashp, pnum, A_BITMAP))) + return (1); + hashp->nmaps++; + clearints = ((nbits - 1) >> INT32_T_BYTE_SHIFT) + 1; + clearbytes = clearints << INT32_T_TO_BYTE; + (void)memset((int8_t *)ip, 0, clearbytes); + (void)memset((int8_t *)ip + clearbytes, + 0xFF, hashp->hdr.bsize - clearbytes); + ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); + SETBIT(ip, 0); + hashp->hdr.bitmaps[ndx] = (u_int16_t)pnum; + hashp->mapp[ndx] = ip; + return (0); +} + +static u_int32_t +first_free(map) + u_int32_t map; +{ + u_int32_t i, mask; + + for (mask = 0x1, i = 0; i < BITS_PER_MAP; i++) { + if (!(mask & map)) + return (i); + mask = mask << 1; + } + return (i); +} + +static u_int16_t +overflow_page(hashp) + HTAB *hashp; +{ + u_int32_t *freep; + int32_t bit, first_page, free_bit, free_page, i, in_use_bits, j; + int32_t max_free, offset, splitnum; + u_int16_t addr; +#ifdef DEBUG2 + int32_t tmp1, tmp2; +#endif + + splitnum = hashp->hdr.ovfl_point; + max_free = hashp->hdr.spares[splitnum]; + + free_page = (max_free - 1) >> (hashp->hdr.bshift + BYTE_SHIFT); + free_bit = (max_free - 1) & ((hashp->hdr.bsize << BYTE_SHIFT) - 1); + + /* + * Look through all the free maps to find the first free block. + * The compiler under -Wall will complain that freep may be used + * before being set, however, this loop will ALWAYS get executed + * at least once, so freep is guaranteed to be set. + */ + first_page = hashp->hdr.last_freed >> (hashp->hdr.bshift + BYTE_SHIFT); + for (i = first_page; i <= free_page; i++) { + if (!(freep = fetch_bitmap(hashp, i))) + return (0); + if (i == free_page) + in_use_bits = free_bit; + else + in_use_bits = (hashp->hdr.bsize << BYTE_SHIFT) - 1; + + if (i == first_page) { + bit = hashp->hdr.last_freed & + ((hashp->hdr.bsize << BYTE_SHIFT) - 1); + j = bit / BITS_PER_MAP; + bit = bit & ~(BITS_PER_MAP - 1); + } else { + bit = 0; + j = 0; + } + for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) + if (freep[j] != ALL_SET) + goto found; + } + + /* No Free Page Found */ + hashp->hdr.last_freed = hashp->hdr.spares[splitnum]; + hashp->hdr.spares[splitnum]++; + offset = hashp->hdr.spares[splitnum] - + (splitnum ? hashp->hdr.spares[splitnum - 1] : 0); + +#define OVMSG "HASH: Out of overflow pages. Increase page size\n" + + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (0); + } + hashp->hdr.ovfl_point = splitnum; + hashp->hdr.spares[splitnum] = hashp->hdr.spares[splitnum - 1]; + hashp->hdr.spares[splitnum - 1]--; + offset = 1; + } + /* Check if we need to allocate a new bitmap page. */ + if (free_bit == (hashp->hdr.bsize << BYTE_SHIFT) - 1) { + free_page++; + if (free_page >= NCACHED) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (0); + } + /* + * This is tricky. The 1 indicates that you want the new page + * allocated with 1 clear bit. Actually, you are going to + * allocate 2 pages from this map. The first is going to be + * the map page, the second is the overflow page we were + * looking for. The __ibitmap routine automatically, sets + * the first bit of itself to indicate that the bitmap itself + * is in use. We would explicitly set the second bit, but + * don't have to if we tell __ibitmap not to leave it clear + * in the first place. + */ + if (__ibitmap(hashp, + (int32_t)OADDR_OF(splitnum, offset), 1, free_page)) + return (0); + hashp->hdr.spares[splitnum]++; +#ifdef DEBUG2 + free_bit = 2; +#endif + offset++; + if (offset > SPLITMASK) { + if (++splitnum >= NCACHED) { + (void)write(STDERR_FILENO, + OVMSG, sizeof(OVMSG) - 1); + return (0); + } + hashp->hdr.ovfl_point = splitnum; + hashp->hdr.spares[splitnum] = + hashp->hdr.spares[splitnum - 1]; + hashp->hdr.spares[splitnum - 1]--; + offset = 0; + } + } else { + /* + * Free_bit addresses the last used bit. Bump it to address + * the first available bit. + */ + free_bit++; + SETBIT(freep, free_bit); + } + + /* Calculate address of the new overflow page */ + addr = OADDR_OF(splitnum, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, free_bit, free_page); +#endif + + if (OADDR_TO_PAGE(addr) > MAX_PAGES(hashp)) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (0); + } + return (addr); + +found: + bit = bit + first_free(freep[j]); + SETBIT(freep, bit); +#ifdef DEBUG2 + tmp1 = bit; + tmp2 = i; +#endif + /* + * Bits are addressed starting with 0, but overflow pages are addressed + * beginning at 1. Bit is a bit address number, so we need to increment + * it to convert it to a page number. + */ + bit = 1 + bit + (i * (hashp->hdr.bsize << BYTE_SHIFT)); + if (bit >= hashp->hdr.last_freed) + hashp->hdr.last_freed = bit - 1; + + /* Calculate the split number for this page */ + for (i = 0; i < splitnum && (bit > hashp->hdr.spares[i]); i++); + offset = (i ? bit - hashp->hdr.spares[i - 1] : bit); + if (offset >= SPLITMASK) + return (0); /* Out of overflow pages */ + addr = OADDR_OF(i, offset); +#ifdef DEBUG2 + (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", + addr, tmp1, tmp2); +#endif + + if (OADDR_TO_PAGE(addr) > MAX_PAGES(hashp)) { + (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); + return (0); + } + /* Allocate and return the overflow page */ + return (addr); +} + +#ifdef DEBUG +int +bucket_to_page(hashp, n) + HTAB *hashp; + int n; +{ + int ret_val; + + ret_val = n + hashp->hdr.hdrpages; + if (n != 0) + ret_val += hashp->hdr.spares[__log2(n + 1) - 1]; + return (ret_val); +} + +int32_t +oaddr_to_page(hashp, n) + HTAB *hashp; + int n; +{ + int ret_val, temp; + + temp = (1 << SPLITNUM(n)) - 1; + ret_val = bucket_to_page(hashp, temp); + ret_val += (n & SPLITMASK); + + return (ret_val); +} +#endif /* DEBUG */ + +indx_t +page_to_oaddr(hashp, pgno) + HTAB *hashp; + db_pgno_t pgno; +{ + int32_t sp, ret_val; + + /* + * To convert page number to overflow address: + * + * 1. Find a starting split point -- use 0 since there are only + * 32 split points. + * 2. Find the split point s.t. 2^sp + hdr.spares[sp] < pgno and + * 2^(sp+1) = hdr.spares[sp+1] > pgno. The overflow address will + * be located at sp. + * 3. return... + */ + pgno -= hashp->hdr.hdrpages; + for (sp = 0; sp < NCACHED; sp++) + if (POW2(sp) + hashp->hdr.spares[sp] < pgno && + (POW2(sp + 1) + hashp->hdr.spares[sp + 1]) > pgno) + break; + + ret_val = OADDR_OF(sp + 1, + pgno - ((POW2(sp + 1) - 1) + hashp->hdr.spares[sp])); +#ifdef DEBUG + assert(OADDR_TO_PAGE(ret_val) == (pgno + hashp->hdr.hdrpages)); +#endif + return (ret_val); +} + +/* + * Mark this overflow page as free. + */ +extern void +__free_ovflpage(hashp, pagep) + HTAB *hashp; + PAGE16 *pagep; +{ + u_int32_t *freep; + int32_t bit_address, free_page, free_bit; + u_int16_t addr, ndx; + + addr = page_to_oaddr(hashp, ADDR(pagep)); + +#ifdef DEBUG2 + (void)fprintf(stderr, "Freeing %d\n", addr); +#endif + ndx = ((u_int16_t)addr) >> SPLITSHIFT; + bit_address = + (ndx ? hashp->hdr.spares[ndx - 1] : 0) + (addr & SPLITMASK) - 1; + if (bit_address < hashp->hdr.last_freed) + hashp->hdr.last_freed = bit_address; + free_page = (bit_address >> (hashp->hdr.bshift + BYTE_SHIFT)); + free_bit = bit_address & ((hashp->hdr.bsize << BYTE_SHIFT) - 1); + + freep = fetch_bitmap(hashp, free_page); +#ifdef DEBUG + /* + * This had better never happen. It means we tried to read a bitmap + * that has already had overflow pages allocated off it, and we + * failed to read it from the file. + */ + if (!freep) + assert(0); +#endif + CLRBIT(freep, free_bit); +#ifdef DEBUG2 + (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", + obufp->addr, free_bit, free_page); +#endif +} + +static u_int32_t * +fetch_bitmap(hashp, ndx) + HTAB *hashp; + int32_t ndx; +{ + if (ndx >= hashp->nmaps) + return (NULL); + if (!hashp->mapp[ndx]) + hashp->mapp[ndx] = (u_int32_t *)__get_page(hashp, + hashp->hdr.bitmaps[ndx], A_BITMAP); + + return (hashp->mapp[ndx]); +} + +#ifdef DEBUG_SLOW +static void +account_page(hashp, pgno, inout) + HTAB *hashp; + db_pgno_t pgno; + int inout; +{ + static struct { + db_pgno_t pgno; + int times; + } list[100]; + static int last; + int i, j; + + if (inout == -1) /* XXX: Kluge */ + inout = 0; + + /* Find page in list. */ + for (i = 0; i < last; i++) + if (list[i].pgno == pgno) + break; + /* Not found. */ + if (i == last) { + list[last].times = inout; + list[last].pgno = pgno; + last++; + } + list[i].times = inout; + if (list[i].times == 0) { + for (j = i; j < last; j++) + list[j] = list[j + 1]; + last--; + } + for (i = 0; i < last; i++, list[i].times++) + if (list[i].times > 20 && !is_bitmap_pgno(hashp, list[i].pgno)) + (void)fprintf(stderr, + "Warning: pg %d has been out for %d times\n", + list[i].pgno, list[i].times); +} +#endif /* DEBUG_SLOW */ diff --git a/src/util/db2/hash/hsearch.c b/src/util/db2/hash/hsearch.c new file mode 100644 index 000000000..f257cd6ea --- /dev/null +++ b/src/util/db2/hash/hsearch.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)hsearch.c 8.5 (Berkeley) 9/21/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include + +#include "db-int.h" +#include "search.h" + +static DB *dbp = NULL; +static ENTRY retval; + +extern int +hcreate(nel) + u_int nel; +{ + HASHINFO info; + + info.nelem = nel; + info.bsize = 256; + info.ffactor = 8; + info.cachesize = 0; + info.hash = NULL; + info.lorder = 0; + dbp = (DB *)__hash_open(NULL, O_CREAT | O_RDWR, 0600, &info, 0); + return (dbp != NULL); +} + +extern ENTRY * +hsearch(item, action) + ENTRY item; + ACTION action; +{ + DBT key, val; + int status; + + if (!dbp) + return (NULL); + key.data = (u_char *)item.key; + key.size = strlen(item.key) + 1; + + if (action == ENTER) { + val.data = (u_char *)item.data; + val.size = strlen(item.data) + 1; + status = (dbp->put)(dbp, &key, &val, R_NOOVERWRITE); + if (status) + return (NULL); + } else { + /* FIND */ + status = (dbp->get)(dbp, &key, &val, 0); + if (status) + return (NULL); + else + item.data = (char *)val.data; + } + retval.key = item.key; + retval.data = item.data; + return (&retval); +} + +extern void +hdestroy() +{ + if (dbp) { + (void)(dbp->close)(dbp); + dbp = NULL; + } +} diff --git a/src/util/db2/hash/page.h b/src/util/db2/hash/page.h new file mode 100644 index 000000000..8ef8a2e29 --- /dev/null +++ b/src/util/db2/hash/page.h @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)page.h 8.4 (Berkeley) 11/7/95 + */ + +#define HI_MASK 0xFFFF0000 +#define LO_MASK (~HI_MASK) + +#define HI(N) ((u_int16_t)(((N) & HI_MASK) >> 16)) +#define LO(N) ((u_int16_t)((N) & LO_MASK)) + +/* Constants for big key page overhead information. */ +#define NUMSHORTS 0 +#define KEYLEN 1 +#define DATALEN 2 +#define NEXTPAGE 3 + +/* + * Hash pages store meta-data beginning at the top of the page (offset 0) + * and key/data values beginning at the bottom of the page (offset pagesize). + * Fields are always accessed via macros so that we can change the page + * format without too much pain. The only changes that will require massive + * code changes are if we no longer store key/data offsets next to each + * other (since we use that fact to compute key lengths). In the accessor + * macros below, P means a pointer to the page, I means an index of the + * particular entry being accessed. + * + * Hash base page format + * BYTE ITEM NBYTES TYPE ACCESSOR MACRO + * ---- ------------------ ------ -------- -------------- + * 0 previous page number 4 db_pgno_t PREV_PGNO(P) + * 4 next page number 4 db_pgno_t NEXT_PGNO(P) + * 8 # pairs on page 2 indx_t NUM_ENT(P) + * 10 page type 1 u_int8_t TYPE(P) + * 11 padding 1 u_int8_t none + * 12 highest free byte 2 indx_t OFFSET(P) + * 14 key offset 0 2 indx_t KEY_OFF(P, I) + * 16 data offset 0 2 indx_t DATA_OFF(P, I) + * 18 key offset 1 2 indx_t KEY_OFF(P, I) + * 20 data offset 1 2 indx_t DATA_OFF(P, I) + * ...etc... + */ + +/* Indices (in bytes) of the beginning of each of these entries */ +#define I_PREV_PGNO 0 +#define I_NEXT_PGNO 4 +#define I_ENTRIES 8 +#define I_TYPE 10 +#define I_HF_OFFSET 12 + +/* Overhead is everything prior to the first key/data pair. */ +#define PAGE_OVERHEAD (I_HF_OFFSET + sizeof(indx_t)) + +/* To allocate a pair, we need room for one key offset and one data offset. */ +#define PAIR_OVERHEAD ((sizeof(indx_t) << 1)) + +/* Use this macro to extract a value of type T from page P at offset O. */ +#define REFERENCE(P, T, O) (((T *)((u_int8_t *)(P) + O))[0]) + +/* + * Use these macros to access fields on a page; P is a PAGE16 *. + */ +#define NUM_ENT(P) (REFERENCE((P), indx_t, I_ENTRIES)) +#define PREV_PGNO(P) (REFERENCE((P), db_pgno_t, I_PREV_PGNO)) +#define NEXT_PGNO(P) (REFERENCE((P), db_pgno_t, I_NEXT_PGNO)) +#define TYPE(P) (REFERENCE((P), u_int8_t, I_TYPE)) +#define OFFSET(P) (REFERENCE((P), indx_t, I_HF_OFFSET)) +/* + * We need to store a page's own address on each page (unlike the Btree + * access method which needs the previous page). We use the PREV_PGNO + * field to store our own page number. + */ +#define ADDR(P) (PREV_PGNO((P))) + +/* Extract key/data offsets and data for a given index. */ +#define DATA_OFF(P, N) \ + REFERENCE(P, indx_t, PAGE_OVERHEAD + N * PAIR_OVERHEAD + sizeof(indx_t)) +#define KEY_OFF(P, N) \ + REFERENCE(P, indx_t, PAGE_OVERHEAD + N * PAIR_OVERHEAD) + +#define KEY(P, N) (((PAGE8 *)(P)) + KEY_OFF((P), (N))) +#define DATA(P, N) (((PAGE8 *)(P)) + DATA_OFF((P), (N))) + +/* + * Macros used to compute various sizes on a page. + */ +#define PAIRSIZE(K, D) (PAIR_OVERHEAD + (K)->size + (D)->size) +#define BIGOVERHEAD (4 * sizeof(u_int16_t)) +#define KEYSIZE(K) (4 * sizeof(u_int16_t) + (K)->size); +#define OVFLSIZE (2 * sizeof(u_int16_t)) +#define BIGPAGEOVERHEAD (4 * sizeof(u_int16_t)) +#define BIGPAGEOFFSET 4 +#define BIGPAGESIZE(P) ((P)->BSIZE - BIGPAGEOVERHEAD) + +#define PAGE_META(N) (((N) + 3) * sizeof(u_int16_t)) +#define MINFILL 0.75 +#define ISBIG(N, P) (((N) > ((P)->hdr.bsize * MINFILL)) ? 1 : 0) + +#define ITEMSIZE(I) (sizeof(u_int16_t) + (I)->size) + +/* + * Big key/data pages use a different page format. They have a single + * key/data "pair" containing the length of the key and data instead + * of offsets. + */ +#define BIGKEYLEN(P) (KEY_OFF((P), 0)) +#define BIGDATALEN(P) (DATA_OFF((P), 0)) +#define BIGKEY(P) (((PAGE8 *)(P)) + PAGE_OVERHEAD + PAIR_OVERHEAD) +#define BIGDATA(P) \ + (((PAGE8 *)(P)) + PAGE_OVERHEAD + PAIR_OVERHEAD + KEY_OFF((P), 0)) + + +#define OVFLPAGE 0 +#define BIGPAIR 0 +#define INVALID_PGNO 0xFFFFFFFF + +typedef unsigned short PAGE16; +typedef unsigned char PAGE8; + +#define A_BUCKET 0 +#define A_OVFL 1 +#define A_BITMAP 2 +#define A_RAW 4 +#define A_HEADER 5 + +#define PAIRFITS(P,K,D) ((PAIRSIZE((K),(D))) <= FREESPACE((P))) +#define BIGPAIRFITS(P) ((FREESPACE((P)) >= PAIR_OVERHEAD)) +/* + * Since these are all unsigned, we need to guarantee that we never go + * negative. Offset values are 0-based and overheads are one based (i.e. + * one byte of overhead is 1, not 0), so we need to convert OFFSETs to + * 1-based counting before subtraction. + */ +#define FREESPACE(P) \ + ((OFFSET((P)) + 1 - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) + +/* + * Overhead on header pages is just one word -- the length of the + * header info stored on that page. + */ +#define HEADER_OVERHEAD 4 + +#define HASH_PAGE 2 +#define HASH_BIGPAGE 3 +#define HASH_OVFLPAGE 4 diff --git a/src/util/db2/hash/page.h.patch b/src/util/db2/hash/page.h.patch new file mode 100644 index 000000000..4a0311fea --- /dev/null +++ b/src/util/db2/hash/page.h.patch @@ -0,0 +1,42 @@ +*** /tmp/,RCSt1a21720 Wed Apr 3 11:49:55 1996 +--- page.h Wed Apr 3 08:42:25 1996 +*************** +*** 158,163 + + #define PAIRFITS(P,K,D) ((PAIRSIZE((K),(D))) <= FREESPACE((P))) + #define BIGPAIRFITS(P) ((FREESPACE((P)) >= PAIR_OVERHEAD)) + #define FREESPACE(P) \ + ((OFFSET((P)) - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) + + +--- 158,169 ----- + + #define PAIRFITS(P,K,D) ((PAIRSIZE((K),(D))) <= FREESPACE((P))) + #define BIGPAIRFITS(P) ((FREESPACE((P)) >= PAIR_OVERHEAD)) ++ /* ++ * Since these are all unsigned, we need to guarantee that we never go ++ * negative. Offset values are 0-based and overheads are one based (i.e. ++ * one byte of overhead is 1, not 0), so we need to convert OFFSETs to ++ * 1-based counting before subtraction. ++ */ + #define FREESPACE(P) \ + ((OFFSET((P)) + 1 - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) + +*************** +*** 159,165 + #define PAIRFITS(P,K,D) ((PAIRSIZE((K),(D))) <= FREESPACE((P))) + #define BIGPAIRFITS(P) ((FREESPACE((P)) >= PAIR_OVERHEAD)) + #define FREESPACE(P) \ +! ((OFFSET((P)) - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) + + /* + * Overhead on header pages is just one word -- the length of the + +--- 165,171 ----- + * 1-based counting before subtraction. + */ + #define FREESPACE(P) \ +! ((OFFSET((P)) + 1 - PAGE_OVERHEAD - (NUM_ENT((P)) * PAIR_OVERHEAD))) + + /* + * Overhead on header pages is just one word -- the length of the diff --git a/src/util/db2/hash/search.h b/src/util/db2/hash/search.h new file mode 100644 index 000000000..4d3b9143e --- /dev/null +++ b/src/util/db2/hash/search.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)search.h 8.1 (Berkeley) 6/4/93 + */ + +/* Backward compatibility to hsearch interface. */ +typedef struct entry { + char *key; + char *data; +} ENTRY; + +typedef enum { + FIND, ENTER +} ACTION; + +int hcreate __P((unsigned int)); +void hdestroy __P((void)); +ENTRY *hsearch __P((ENTRY, ACTION)); diff --git a/src/util/db2/include/db-int.h b/src/util/db2/include/db-int.h new file mode 100644 index 000000000..78be4c8a3 --- /dev/null +++ b/src/util/db2/include/db-int.h @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)compat.h 8.13 (Berkeley) 2/21/94 + */ + +#ifndef _DB_INT_H_ +#define _DB_INT_H_ + +#include "db.h" + +/* deal with autoconf-based stuff (db.h includes db-config.h) */ + +#ifndef HAVE_MEMMOVE +#define memmove my_memmove +#endif + +#ifndef HAVE_MKSTEMP +#define mkstemp my_mkstemp +#endif + +#ifndef HAVE_STRERROR +#define strerror my_strerror +#endif + +#define DB_LITTLE_ENDIAN 1234 +#define DB_BIG_ENDIAN 4321 + +#ifdef WORDS_BIGENDIAN +#define DB_BYTE_ORDER DB_BIG_ENDIAN +#else +#define DB_BYTE_ORDER DB_LITTLE_ENDIAN +#endif + +/* end autoconf-based stuff */ + +/* include necessary system header files */ + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +/* types and constants used for database structure */ + +#define MAX_PAGE_NUMBER 0xffffffff /* >= # of pages in a file */ +typedef u_int32_t db_pgno_t; +#define MAX_PAGE_OFFSET 65535 /* >= # of bytes in a page */ +typedef u_int16_t indx_t; +#define MAX_REC_NUMBER 0xffffffff /* >= # of records in a tree */ +typedef u_int32_t recno_t; + +/* + * Little endian <==> big endian 32-bit swap macros. + * M_32_SWAP swap a memory location + * P_32_SWAP swap a referenced memory location + * P_32_COPY swap from one location to another + */ +#define M_32_SWAP(a) { \ + u_int32_t _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[3]; \ + ((char *)&a)[1] = ((char *)&_tmp)[2]; \ + ((char *)&a)[2] = ((char *)&_tmp)[1]; \ + ((char *)&a)[3] = ((char *)&_tmp)[0]; \ +} +#define P_32_SWAP(a) { \ + u_int32_t _tmp = *(u_int32_t *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[3]; \ + ((char *)a)[1] = ((char *)&_tmp)[2]; \ + ((char *)a)[2] = ((char *)&_tmp)[1]; \ + ((char *)a)[3] = ((char *)&_tmp)[0]; \ +} +#define P_32_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[3]; \ + ((char *)&(b))[1] = ((char *)&(a))[2]; \ + ((char *)&(b))[2] = ((char *)&(a))[1]; \ + ((char *)&(b))[3] = ((char *)&(a))[0]; \ +} + +/* + * Little endian <==> big endian 16-bit swap macros. + * M_16_SWAP swap a memory location + * P_16_SWAP swap a referenced memory location + * P_16_COPY swap from one location to another + */ +#define M_16_SWAP(a) { \ + u_int16_t _tmp = a; \ + ((char *)&a)[0] = ((char *)&_tmp)[1]; \ + ((char *)&a)[1] = ((char *)&_tmp)[0]; \ +} +#define P_16_SWAP(a) { \ + u_int16_t _tmp = *(u_int16_t *)a; \ + ((char *)a)[0] = ((char *)&_tmp)[1]; \ + ((char *)a)[1] = ((char *)&_tmp)[0]; \ +} +#define P_16_COPY(a, b) { \ + ((char *)&(b))[0] = ((char *)&(a))[1]; \ + ((char *)&(b))[1] = ((char *)&(a))[0]; \ +} + +/* open functions for each database type, used in dbopen() */ + +DB *__bt_open __P((const char *, int, int, const BTREEINFO *, int)); +DB *__hash_open __P((const char *, int, int, const HASHINFO *, int)); +DB *__rec_open __P((const char *, int, int, const RECNOINFO *, int)); +void __dbpanic __P((DB *dbp)); + +/* + * There is no portable way to figure out the maximum value of a file + * offset, so we put it here. + */ +#ifdef OFF_T_MAX +#define DB_OFF_T_MAX OFF_T_MAX +#else +#define DB_OFF_T_MAX LONG_MAX +#endif + +#ifndef O_ACCMODE /* POSIX 1003.1 access mode mask. */ +#define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) +#endif + +/* + * If you can't provide lock values in the open(2) call. Note, this + * allows races to happen. + */ +#ifndef O_EXLOCK /* 4.4BSD extension. */ +#define O_EXLOCK 0 +#endif + +#ifndef O_SHLOCK /* 4.4BSD extension. */ +#define O_SHLOCK 0 +#endif + +#ifndef EFTYPE +#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */ +#endif + +#ifndef STDERR_FILENO +#define STDIN_FILENO 0 /* ANSI C #defines */ +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#endif + +#ifndef SEEK_END +#define SEEK_SET 0 /* POSIX 1003.1 seek values */ +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + +#ifndef NULL /* ANSI C #defines NULL everywhere. */ +#define NULL 0 +#endif + +#ifndef MAX /* Usually found in . */ +#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a)) +#endif +#ifndef MIN /* Usually found in . */ +#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b)) +#endif + +#ifndef S_ISDIR /* POSIX 1003.1 file type tests. */ +#define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) ((m & 0170000) == 0010000) /* fifo */ +#endif +#ifndef S_ISLNK /* BSD POSIX 1003.1 extensions */ +#define S_ISLNK(m) ((m & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) ((m & 0170000) == 0140000) /* socket */ +#endif + +#endif /* _DB_INT_H_ */ diff --git a/src/util/db2/include/db-ndbm.h b/src/util/db2/include/db-ndbm.h new file mode 100644 index 000000000..1bafb6c4b --- /dev/null +++ b/src/util/db2/include/db-ndbm.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)ndbm.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _NDBM_H_ +#define _NDBM_H_ + +#include "db.h" + +/* Map dbm interface onto db(3). */ +#define DBM_RDONLY O_RDONLY + +/* Flags to dbm_store(). */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * The db(3) support for ndbm(3) always appends this suffix to the + * file name to avoid overwriting the user's original database. + */ +#define DBM_SUFFIX ".db" + +typedef struct { + char *dptr; + int dsize; +} datum; + +typedef DB DBM; +#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE + +__BEGIN_DECLS +void dbm_close __P((DBM *)); +int dbm_delete __P((DBM *, datum)); +datum dbm_fetch __P((DBM *, datum)); +datum dbm_firstkey __P((DBM *)); +long dbm_forder __P((DBM *, datum)); +datum dbm_nextkey __P((DBM *)); +DBM *dbm_open __P((const char *, int, int)); +int dbm_store __P((DBM *, datum, datum, int)); +int dbm_dirfno __P((DBM *)); +__END_DECLS + +#endif /* !_NDBM_H_ */ diff --git a/src/util/db2/include/db-queue.h b/src/util/db2/include/db-queue.h new file mode 100644 index 000000000..40d32ccb6 --- /dev/null +++ b/src/util/db2/include/db-queue.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.3 (Berkeley) 12/13/93 + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +/* + * This file defines three types of data structures: lists, tail queues, + * and circular queues. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element or at the head of the list. A list may only be + * traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A tail queue may only be traversed in the forward direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) { \ + (head)->lh_first = NULL; \ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} + +#define LIST_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} + +#define LIST_REMOVE(elm, field) { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_HEAD(head, elm, field) { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} + +#define TAILQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} + +#define TAILQ_REMOVE(head, elm, field) { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ +} + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} + +#define CIRCLEQ_REMOVE(head, elm, field) { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} +#endif /* !_QUEUE_H_ */ diff --git a/src/util/db2/include/db.h b/src/util/db2/include/db.h new file mode 100644 index 000000000..6b5c04e6b --- /dev/null +++ b/src/util/db2/include/db.h @@ -0,0 +1,170 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)db.h 8.8 (Berkeley) 11/2/95 + */ + +#ifndef _DB_H_ +#define _DB_H_ + +#include + +#include + +#define RET_ERROR -1 /* Return values. */ +#define RET_SUCCESS 0 +#define RET_SPECIAL 1 + +/* Key/data structure -- a Data-Base Thang. */ +typedef struct { + void *data; /* data */ + size_t size; /* data length */ +} DBT; + +/* Routine flags. */ +#define R_CURSOR 1 /* del, put, seq */ +#define __R_UNUSED 2 /* UNUSED */ +#define R_FIRST 3 /* seq */ +#define R_IAFTER 4 /* put (RECNO) */ +#define R_IBEFORE 5 /* put (RECNO) */ +#define R_LAST 6 /* seq (BTREE, RECNO) */ +#define R_NEXT 7 /* seq */ +#define R_NOOVERWRITE 8 /* put */ +#define R_PREV 9 /* seq (BTREE, RECNO) */ +#define R_SETCURSOR 10 /* put (RECNO) */ +#define R_RECNOSYNC 11 /* sync (RECNO) */ + +typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE; + +/* + * !!! + * The following flags are included in the dbopen(3) call as part of the + * open(2) flags. In order to avoid conflicts with the open flags, start + * at the top of the 16 or 32-bit number space and work our way down. If + * the open flags were significantly expanded in the future, it could be + * a problem. Wish I'd left another flags word in the dbopen call. + * + * !!! + * None of this stuff is implemented yet. The only reason that it's here + * is so that the access methods can skip copying the key/data pair when + * the DB_LOCK flag isn't set. + */ +#if UINT_MAX > 65535 +#define DB_LOCK 0x20000000 /* Do locking. */ +#define DB_SHMEM 0x40000000 /* Use shared memory. */ +#define DB_TXN 0x80000000 /* Do transactions. */ +#else +#define DB_LOCK 0x2000 /* Do locking. */ +#define DB_SHMEM 0x4000 /* Use shared memory. */ +#define DB_TXN 0x8000 /* Do transactions. */ +#endif + +/* deal with turning prototypes on and off */ + +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#endif + +/* Access method description structure. */ +typedef struct __db { + DBTYPE type; /* Underlying db type. */ + int (*close) __P((struct __db *)); + int (*del) __P((const struct __db *, const DBT *, u_int)); + int (*get) __P((const struct __db *, const DBT *, DBT *, u_int)); + int (*put) __P((const struct __db *, DBT *, const DBT *, u_int)); + int (*seq) __P((const struct __db *, DBT *, DBT *, u_int)); + int (*sync) __P((const struct __db *, u_int)); + void *internal; /* Access method private. */ + int (*fd) __P((const struct __db *)); +} DB; + +#define BTREEMAGIC 0x053162 +#define BTREEVERSION 3 + +/* Structure used to pass parameters to the btree routines. */ +typedef struct { +#define R_DUP 0x01 /* duplicate keys */ + u_long flags; + u_int cachesize; /* bytes to cache */ + int maxkeypage; /* maximum keys per page */ + int minkeypage; /* minimum keys per page */ + u_int psize; /* page size */ + int (*compare) /* comparison function */ + __P((const DBT *, const DBT *)); + size_t (*prefix) /* prefix function */ + __P((const DBT *, const DBT *)); + int lorder; /* byte order */ +} BTREEINFO; + +#define HASHMAGIC 0x061561 +#define HASHVERSION 3 + +/* Structure used to pass parameters to the hashing routines. */ +typedef struct { + u_int bsize; /* bucket size */ + u_int ffactor; /* fill factor */ + u_int nelem; /* number of elements */ + u_int cachesize; /* bytes to cache */ + u_int32_t /* hash function */ + (*hash) __P((const void *, size_t)); + int lorder; /* byte order */ +} HASHINFO; + +/* Structure used to pass parameters to the record routines. */ +typedef struct { +#define R_FIXEDLEN 0x01 /* fixed-length records */ +#define R_NOKEY 0x02 /* key not required */ +#define R_SNAPSHOT 0x04 /* snapshot the input */ + u_long flags; + u_int cachesize; /* bytes to cache */ + u_int psize; /* page size */ + int lorder; /* byte order */ + size_t reclen; /* record length (fixed-length records) */ + u_char bval; /* delimiting byte (variable-length records */ + char *bfname; /* btree file name */ +} RECNOINFO; + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS }; +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +__BEGIN_DECLS +DB *dbopen __P((const char *, int, int, DBTYPE, const void *)); +__END_DECLS + +#endif /* !_DB_H_ */ diff --git a/src/util/db2/man/Makefile.inc b/src/util/db2/man/Makefile.inc new file mode 100644 index 000000000..f85cba1f8 --- /dev/null +++ b/src/util/db2/man/Makefile.inc @@ -0,0 +1,7 @@ +# @(#)Makefile.inc 8.2 (Berkeley) 11/14/94 + +.PATH: ${.CURDIR}/db/man + +MAN3+= db_btree.0 db_hash.0 db_lock.0 db_log.0 db_mpool.0 db_open.0 \ + db_recno.0 +MLINKS+=db_open.3 db.3 db_open.3 dbopen.3 diff --git a/src/util/db2/man/db.man.ps b/src/util/db2/man/db.man.ps new file mode 100644 index 000000000..23feea6e5 --- /dev/null +++ b/src/util/db2/man/db.man.ps @@ -0,0 +1,2295 @@ +%!PS-Adobe-3.0 +%%Creator: groff version 1.08 +%%DocumentNeededResources: font Times-Roman +%%+ font Times-Bold +%%+ font Times-Italic +%%DocumentSuppliedResources: procset grops 1.08 0 +%%Pages: 28 +%%PageOrder: Ascend +%%Orientation: Portrait +%%EndComments +%%BeginProlog +%%BeginResource: procset grops 1.08 0 +/setpacking where{ +pop +currentpacking +true setpacking +}if +/grops 120 dict dup begin +/SC 32 def +/A/show load def +/B{0 SC 3 -1 roll widthshow}bind def +/C{0 exch ashow}bind def +/D{0 exch 0 SC 5 2 roll awidthshow}bind def +/E{0 rmoveto show}bind def +/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def +/G{0 rmoveto 0 exch ashow}bind def +/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/I{0 exch rmoveto show}bind def +/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def +/K{0 exch rmoveto 0 exch ashow}bind def +/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/M{rmoveto show}bind def +/N{rmoveto 0 SC 3 -1 roll widthshow}bind def +/O{rmoveto 0 exch ashow}bind def +/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/Q{moveto show}bind def +/R{moveto 0 SC 3 -1 roll widthshow}bind def +/S{moveto 0 exch ashow}bind def +/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def +/SF{ +findfont exch +[exch dup 0 exch 0 exch neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/MF{ +findfont +[5 2 roll +0 3 1 roll +neg 0 0]makefont +dup setfont +[exch/setfont cvx]cvx bind def +}bind def +/level0 0 def +/RES 0 def +/PL 0 def +/LS 0 def +/PLG{ +gsave newpath clippath pathbbox grestore +exch pop add exch pop +}bind def +/BP{ +/level0 save def +1 setlinecap +1 setlinejoin +72 RES div dup scale +LS{ +90 rotate +}{ +0 PL translate +}ifelse +1 -1 scale +}bind def +/EP{ +level0 restore +showpage +}bind def +/DA{ +newpath arcn stroke +}bind def +/SN{ +transform +.25 sub exch .25 sub exch +round .25 add exch round .25 add exch +itransform +}bind def +/DL{ +SN +moveto +SN +lineto stroke +}bind def +/DC{ +newpath 0 360 arc closepath +}bind def +/TM matrix def +/DE{ +TM currentmatrix pop +translate scale newpath 0 0 .5 0 360 arc closepath +TM setmatrix +}bind def +/RC/rcurveto load def +/RL/rlineto load def +/ST/stroke load def +/MT/moveto load def +/CL/closepath load def +/FL{ +currentgray exch setgray fill setgray +}bind def +/BL/fill load def +/LW/setlinewidth load def +/RE{ +findfont +dup maxlength 1 index/FontName known not{1 add}if dict begin +{ +1 index/FID ne{def}{pop pop}ifelse +}forall +/Encoding exch def +dup/FontName exch def +currentdict end definefont pop +}bind def +/DEFS 0 def +/EBEGIN{ +moveto +DEFS begin +}bind def +/EEND/end load def +/CNT 0 def +/level1 0 def +/PBEGIN{ +/level1 save def +translate +div 3 1 roll div exch scale +neg exch neg exch translate +0 setgray +0 setlinecap +1 setlinewidth +0 setlinejoin +10 setmiterlimit +[]0 setdash +/setstrokeadjust where{ +pop +false setstrokeadjust +}if +/setoverprint where{ +pop +false setoverprint +}if +newpath +/CNT countdictstack def +userdict begin +/showpage{}def +}bind def +/PEND{ +clear +countdictstack CNT sub{end}repeat +level1 restore +}bind def +end def +/setpacking where{ +pop +setpacking +}if +%%EndResource +%%IncludeResource: font Times-Roman +%%IncludeResource: font Times-Bold +%%IncludeResource: font Times-Italic +grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL +792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron +/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space +/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft +/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four +/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C +/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash +/bracketright/circumflex/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q +/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/tilde/.notdef/quotesinglbase +/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger +/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut +/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash +/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar +/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus +/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu +/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright +/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde +/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute +/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis +/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls +/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute +/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve +/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex +/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE +/Times-Bold@0 ENC0/Times-Bold RE/Times-Roman@0 ENC0/Times-Roman RE +%%EndProlog +%%Page: 1 1 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 113.45(DB_BTREE\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 113.45(anual DB_BTREE\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA) +72 84 S(ME).18 E F0(db_btree \255 btree database access method)108 96 Q F1 +(DESCRIPTION)72 112.8 Q F0 .486(The DB library is a f)108 124.8 R .485 +(amily of groups of functions that pro)-.1 F .485 +(vides a modular programming interf)-.15 F .485(ace to trans-)-.1 F .822 +(actions and record-oriented \214le access.)108 136.8 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 148.8 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 148.8 S .258(ell as v)237.082 148.8 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 148.8 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 148.8 S .258 +(he functional groups \(e.g.)436.746 148.8 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 160.8 R .306(tional groups are e)108 172.8 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 172.8 Q .245(description of transactions, see)108 184.8 R/F2 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F2(db_open)2.745 E +F0(\(3\)).24 E .308(and then the indi)108 196.8 R .308 +(vidual access method manual pages:)-.25 F F2(db_btr)2.807 E(ee)-.37 E F0 +(\(3\),).18 E F2(db_hash)2.807 E F0(\(3\),).28 E F2(db_lo)2.807 E(g)-.1 E F0 +.307(\(3\) and).22 F F2(db_r)2.807 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +208.8 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +208.8 R 3.635(,s)-.4 G(ee)307.32 208.8 Q F2(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 220.8 Q 2.5(,s)-.4 G(ee)171.2 220.8 Q F2(db_mpool)2.5 E F0 +(\(3\).).51 E +(This manual page describes speci\214c details of the btree access method.)108 +237.6 Q 1.518(The btree data structure is a sorted, balanced tree structure st\ +oring associated k)108 254.4 R -.15(ey)-.1 G 1.517(/data pairs.).15 F +(Searches,)6.517 E .598(insertions, and deletions in the btree will all comple\ +te in O lg base N where base is the a)108 266.4 R -.15(ve)-.2 G .598 +(rage \214ll f).15 F(actor)-.1 E(.)-.55 E .306 +(Often, inserting ordered data into btrees results in a lo)108 278.4 R 2.806 +<778c>-.25 G .305(ll f)341.61 278.4 R(actor)-.1 E 5.305(.T)-.55 G .305 +(his implementation has been modi\214ed)386.56 278.4 R(to mak)108 290.4 Q 2.5 +(eo)-.1 G(rdered insertion the best case, resulting in a much better than norm\ +al page \214ll f)147.34 290.4 Q(actor)-.1 E(.)-.55 E F1 -.495(AC)72 307.2 S +(CESS METHOD SPECIFIC INFORMA).495 E(TION)-.855 E F0 .175 +(The btree access method speci\214c data structure pro)108 319.2 R .176 +(vided to)-.15 F F2(db_open)2.676 E F0 .176(is typedef)2.676 F 1.176 -.5('d a) +.55 H .176(nd named BTREEINFO.).5 F 2.638(AB)108 331.2 S .138 +(TREEINFO structure has at least the follo)124.528 331.2 R .137 +(wing \214elds, which may be initialized before calling)-.25 F F2(db_open)2.637 +E F0(:).24 E(u_int cachesize;)108 348 Q 3.743(As)133 360 S 1.243 +(uggested maximum size \(in bytes\) of the memory cache.)147.853 360 R 1.243 +(This v)6.243 F 1.243(alue is)-.25 F/F3 10/Times-Bold@0 SF(only)3.743 E F0 +(advisory)3.743 E 3.744(,a)-.65 G 1.244(nd the)514.036 360 R .017 +(access method will allocate more memory rather than f)133 372 R 2.517 +(ail. Since)-.1 F -2.15 -.25(ev e)2.517 H .016(ry search e).25 F .016 +(xamines the root page)-.15 F 1.319 +(of the tree, caching the most recently used pages substantially impro)133 384 +R -.15(ve)-.15 G 3.82(sa).15 G 1.32(ccess time.)441.05 384 R 1.32(In addition,) +6.32 F(ph)133 396 Q .911(ysical writes are delayed as long as possible, so a m\ +oderate cache can reduce the number of I/O)-.05 F 1.497 +(operations signi\214cantly)133 408 R 6.497(.O)-.65 G -.15(bv)243.674 408 S +(iously).15 E 3.997(,u)-.65 G 1.497(sing a cache increases \(b)288.821 408 R +1.498(ut only increases\) the lik)-.2 F 1.498(elihood of)-.1 F .336(corruption\ + or lost data if the system crashes while a tree is being modi\214ed.)133 420 R +(If)5.336 E F2(cac)2.836 E(hesize)-.15 E F0 .335(is 0 \(no size)2.835 F +(is speci\214ed\) a def)133 432 Q(ault cache is used.)-.1 E +(int \(*compare\)\(const DBT *, const DBT *\);)108 448.8 Q .194 +(Compare is the k)133 460.8 R .494 -.15(ey c)-.1 H .194(omparison function.).15 +F .194(It must return an inte)5.194 F .194 +(ger less than, equal to, or greater than)-.15 F .656(zero if the \214rst k)133 +472.8 R .956 -.15(ey a)-.1 H -.18(rg).15 G .656 +(ument is considered to be respecti).18 F -.15(ve)-.25 G .655 +(ly less than, equal to, or greater than the).15 F .798(second k)133 484.8 R +1.098 -.15(ey a)-.1 H -.18(rg).15 G 3.298(ument. The).18 F .798 +(same comparison function must be used on a gi)3.298 F -.15(ve)-.25 G 3.298(nt) +.15 G .799(ree e)462.774 484.8 R -.15(ve)-.25 G .799(ry time it is).15 F 2.79 +(opened. If)133 496.8 R F2(compar)2.79 E(e)-.37 E F0 .29 +(is NULL \(no comparison function is speci\214ed\), the k)2.79 F -.15(ey)-.1 G +2.79(sa).15 G .29(re compared le)451.08 496.8 R(xically)-.15 E(,)-.65 E +(with shorter k)133 508.8 Q -.15(ey)-.1 G 2.5(sc).15 G +(onsidered less than longer k)208.57 508.8 Q -.15(ey)-.1 G(s.).15 E +(u_long \215ags;)108 525.6 Q(The \215ag v)133 537.6 Q(alue is speci\214ed by) +-.25 E F2(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)302.2 537.6 S +(he follo)310.81 537.6 Q(wing v)-.25 E(alues:)-.25 E(R_DUP)133 554.4 Q .354 +(Permit duplicate k)158 566.4 R -.15(ey)-.1 G 2.854(si).15 G 2.854(nt)250.752 +566.4 S .355(he tree, i.e. permit insertion if the k)261.386 566.4 R .655 -.15 +(ey t)-.1 H 2.855(ob).15 G 2.855(ei)432.64 566.4 S .355(nserted already e) +442.715 566.4 R .355(xists in)-.15 F 1.65(the tree.)158 578.4 R 1.65(The def) +6.65 F 1.65(ault beha)-.1 F(vior)-.2 E 4.149(,a)-.4 G 4.149(sd)295.509 578.4 S +1.649(escribed in)308.548 578.4 R F2(db_open)4.149 E F0 1.649(\(3\), is to o) +.24 F -.15(ve)-.15 G 1.649(rwrite a matching k).15 F -.15(ey)-.1 G .783 +(when inserting a ne)158 590.4 R 3.283(wk)-.25 G 1.083 -.15(ey o)253.542 590.4 +T 3.283(rt).15 G 3.283(of)280.508 590.4 S .783(ail if the R_NOO)292.021 590.4 R +(VER)-.5 E .784(WRITE \215ag is speci\214ed.)-.55 F .784(The R_DUP)5.784 F .129 +(\215ag is o)158 602.4 R -.15(ve)-.15 G .129(rridden by the R_NOO).15 F(VER)-.5 +E .128(WRITE \215ag, and if the R_NOO)-.55 F(VER)-.5 E .128 +(WRITE \215ag is spec-)-.55 F(i\214ed, attempts to insert duplicate k)158 614.4 +Q -.15(ey)-.1 G 2.5(si).15 G(nto the tree will f)314.69 614.4 Q(ail.)-.1 E .835 +(If the database contains duplicate k)158 631.2 R -.15(ey)-.1 G .835 +(s, the order of retrie).15 F -.25(va)-.25 G 3.335(lo).25 G 3.336(fk)414.7 +631.2 S -.15(ey)426.266 631.2 S .836(/data pairs is unde\214ned if).15 F(the) +158 643.2 Q F2 -.1(ge)3.003 G(t).1 E F0 .503(function is used, ho)3.003 F(we) +-.25 E -.15(ve)-.25 G -.4(r,).15 G F2(seq)3.403 E F0 .502 +(function calls with the R_CURSOR \215ag set will al)3.003 F -.1(wa)-.1 G(ys).1 +E(return the logical `)158 655.2 Q(`\214rst')-.74 E 2.5('o)-.74 G 2.5(fa)263.72 +655.2 S .3 -.15(ny g)273.99 655.2 T(roup of duplicate k).15 E -.15(ey)-.1 G(s.) +.15 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 2 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 113.45(DB_BTREE\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 113.45(anual DB_BTREE\(3\))340.17 48 R(int lorder;)108 84 Q .65 +(The byte order for inte)133 96 R .65(gers in the stored database metadata.) +-.15 F .65(The number should represent the order)5.65 F .749(as an inte)133 108 +R .749(ger; for e)-.15 F .749(xample, big endian order w)-.15 F .749 +(ould be the number 4,321.)-.1 F(If)5.749 E/F1 10/Times-Italic@0 SF(lor)3.249 E +(der)-.37 E F0 .749(is 0 \(no order is)3.249 F +(speci\214ed\) the current host order is used.)133 120 Q(int maxk)108 136.8 Q +-.15(ey)-.1 G(page;).15 E .073(The maximum number of k)133 148.8 R -.15(ey)-.1 +G 2.573(sw).15 G .073(hich will be stored on an)266.155 148.8 R 2.574(ys)-.15 G +.074(ingle page.)376.436 148.8 R .074(This functionality is not cur)5.074 F(-) +-.2 E(rently implemented.)133 160.8 Q(int mink)108 177.6 Q -.15(ey)-.1 G(page;) +.15 E .532(The minimum number of k)133 189.6 R -.15(ey)-.1 G 3.031(sw).15 G +.531(hich will be stored on an)266.787 189.6 R 3.031(ys)-.15 G .531 +(ingle page.)379.813 189.6 R .531(This v)5.531 F .531(alue is used to deter) +-.25 F(-)-.2 E .558(mine which k)133 201.6 R -.15(ey)-.1 G 3.058(sw).15 G .558 +(ill be stored on o)211.914 201.6 R -.15(ve)-.15 G(r\215o).15 E 3.058(wp)-.25 G +.558(ages, i.e. if a k)319.424 201.6 R .859 -.15(ey o)-.1 H 3.059(rd).15 G .559 +(ata item is longer than the page-)408.336 201.6 R .063(size di)133 213.6 R +.063(vided by the mink)-.25 F -.15(ey)-.1 G .063(page v).15 F .063 +(alue, it will be stored on o)-.25 F -.15(ve)-.15 G(r\215o).15 E 2.563(wp)-.25 +G .062(ages instead of in the page itself.)408.816 213.6 R(If)133 225.6 Q F1 +(mink)2.5 E -.3(ey)-.1 G(pa).3 E -.1(ge)-.1 G F0(is 0 \(no minimum number of k) +2.6 E -.15(ey)-.1 G 2.5(si).15 G 2.5(ss)332.96 225.6 S(peci\214ed\) a v)343.24 +225.6 Q(alue of 2 is used.)-.25 E +(size_t \(*pre\214x\)\(const DBT *, const DBT *\);)108 242.4 Q .691 +(Pre\214x is the pre\214x comparison function.)133 254.4 R .692 +(If speci\214ed, this function must return the number of bytes)5.691 F .195 +(of the second k)133 266.4 R .495 -.15(ey a)-.1 H -.18(rg).15 G .195 +(ument which are necessary to determine that it is greater than the \214rst k) +.18 F .495 -.15(ey a)-.1 H -.18(rg).15 G(u-).18 E 2.994(ment. If)133 278.4 R +.494(the k)2.994 F -.15(ey)-.1 G 2.994(sa).15 G .494(re equal, the k)211.376 +278.4 R .794 -.15(ey l)-.1 H .494(ength should be returned.).15 F .494 +(Note, the usefulness of this function)5.494 F .327(is v)133 290.4 R .327 +(ery data dependent, b)-.15 F .326(ut, in some data sets can produce signi\214\ +cantly reduced tree sizes and search)-.2 F 2.789(times. If)133 302.4 R F1(pr) +2.789 E(e\214x)-.37 E F0 .289(is NULL \(no pre\214x function is speci\214ed\),) +2.789 F/F2 10/Times-Bold@0 SF(and)2.789 E F0 .29 +(no comparison function is speci\214ed, a)2.79 F(def)133 314.4 Q .902(ault le) +-.1 F .902(xical comparison function is used.)-.15 F(If)5.901 E F1(pr)3.401 E +(e\214x)-.37 E F0 .901(is NULL and a comparison function is speci-)3.401 F +(\214ed, no pre\214x comparison is done.)133 326.4 Q(u_int psize;)108 343.2 Q +-.15(Pa)133 355.2 S .118 +(ge size is the size \(in bytes\) of the pages used for nodes in the tree.).15 +F .119(The minimum page size is 512)5.119 F .377 +(bytes and the maximum page size is 64K.)133 367.2 R(If)5.376 E F1(psize)2.876 +E F0 .376(is 0 \(no page size is speci\214ed\) a page size is cho-)2.876 F +(sen based on the underlying \214le system I/O block size.)133 379.2 Q .79 +(If the \214le already e)108 396 R .79(xists \(and the O_TR)-.15 F .79 +(UNC \215ag is not speci\214ed\), the v)-.4 F .79 +(alues speci\214ed for the parameters)-.25 F +(\215ags, lorder and psize are ignored in f)108 408 Q -.2(avo)-.1 G 2.5(ro).2 G +2.5(ft)284.4 408 S(he v)293.01 408 Q(alues used when the tree w)-.25 E +(as created.)-.1 E/F3 9/Times-Bold@0 SF(DB OPERA)72 424.8 Q(TIONS)-.855 E F0 +1.037(The functions returned by)108 436.8 R F1(db_open)3.537 E F0 1.036 +(for the btree access method are as described in)3.536 F F1(db_open)3.536 E F0 +1.036(\(3\), with the).24 F(follo)108 448.8 Q(wing e)-.25 E +(xceptions and additions:)-.15 E 5.28(type The)108 465.6 R(type is DB_BTREE.) +2.5 E 10.28(del Space)108 482.4 R 1.681(freed up by deleting k)4.181 F -.15(ey) +-.1 G 1.681(/data pairs from the tree is ne).15 F -.15(ve)-.25 G 4.181(rr).15 G +1.682(eclaimed, although it is reused)411.342 482.4 R .734(where possible.)133 +494.4 R .734(This means that the btree storage structure is gro)5.734 F(w-only) +-.25 E 5.734(.T)-.65 G .734(he only solutions are to)443.734 494.4 R -.2(avo) +133 506.4 S(id e).2 E(xcessi)-.15 E .3 -.15(ve d)-.25 H +(eletions, or to create a fresh tree periodically from a scan of an e).15 E +(xisting one.)-.15 E 9.72(put The)108 523.2 R F1(put)2.5 E F0(function tak)2.5 +E(es the follo)-.1 E(wing additional \215ags:)-.25 E(R_SETCURSOR)133 540 Q +(Store the k)158 552 Q -.15(ey)-.1 G(/data pair).15 E 2.5(,s)-.4 G +(etting or initializing the position of the cursor to reference it.)256.5 552 Q +9.17(seq F)108 568.8 R(orw)-.15 E +(ard sequential scans of a tree are from the least k)-.1 E .3 -.15(ey t)-.1 H +2.5(ot).15 G(he greatest.)373.55 568.8 Q .892(The returned k)133 585.6 R 1.192 +-.15(ey f)-.1 H .892(or the).15 F F1(seq)3.393 E F0 .893 +(function is not necessarily an e)3.393 F .893 +(xact match for the speci\214ed k)-.15 F 1.193 -.15(ey i)-.1 H 3.393(nt).15 G +(he)530.56 585.6 Q .5(btree access method.)133 597.6 R .5(The returned k)5.5 F +.8 -.15(ey i)-.1 H 3(st).15 G .499(he smallest k)307.04 597.6 R .799 -.15(ey g) +-.1 H .499(reater than or equal to the speci\214ed k).15 F -.15(ey)-.1 G(,)-.5 +E(permitting partial k)133 609.6 Q .3 -.15(ey m)-.1 H +(atches and range searches.).15 E(The)133 626.4 Q F1(seq)2.5 E F0(function tak) +2.5 E(es the follo)-.1 E(wing additional \215ags:)-.25 E(R_LAST)133 643.2 Q .04 +(The last k)158 655.2 R -.15(ey)-.1 G .04(/data pair of the database is return\ +ed, and the cursor is set or initialized to reference).15 F(it.)158 667.2 Q +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(2)535 732 Q EP +%%Page: 3 3 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 113.45(DB_BTREE\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 113.45(anual DB_BTREE\(3\))340.17 48 R(R_PREV)133 84 Q(Retrie)158 96 +Q .59 -.15(ve t)-.25 H .29(he k).15 F -.15(ey)-.1 G .29 +(/data pair immediately before the cursor).15 F 5.29(.I)-.55 G 2.79(ft)395.73 +96 S .29(he cursor is not yet set, this is the)404.63 96 R +(same as the R_LAST \215ag.)158 108 Q/F1 9/Times-Bold@0 SF(ERR)72 124.8 Q(ORS) +-.27 E F0(The)108 136.8 Q/F2 10/Times-Italic@0 SF(btr)2.541 E(ee)-.37 E F0 .041 +(access method functions may f)2.541 F .041(ail and set)-.1 F F2(errno)2.541 E +F0 .041(for an)2.541 F 2.541(yo)-.15 G 2.541(ft)376.152 136.8 S .041 +(he errors speci\214ed for the library func-)384.803 136.8 R(tion)108 148.8 Q +F2(db_open)2.5 E F0(\(3\).).24 E F1(SEE ALSO)72 165.6 Q F2(db_hash)108 177.6 Q +F0(\(3\),).28 E F2(db_loc)2.5 E(k)-.2 E F0(\(3\),).67 E F2(db_lo)2.5 E(g)-.1 E +F0(\(3\),).22 E F2(db_mpool)2.5 E F0(\(3\),).51 E F2(db_open)2.5 E F0(\(3\),) +.24 E F2(db_r)2.5 E(ecno)-.37 E F0(\(3\),).18 E F2(db_txn)2.5 E F0(\(3\)).24 E +F2(The Ubiquitous B-tr)108 201.6 Q(ee)-.37 E F0 2.5(,D).18 G(ouglas Comer) +209.47 201.6 Q 2.5(,A)-.4 G(CM Comput. Surv)276.72 201.6 Q 2.5(.1)-.65 G +(1, 2 \(June 1979\), 121-138.)360.25 201.6 Q F2(Pr)108 225.6 Q 1.588 +(e\214x B-tr)-.37 F(ees)-.37 E F0 4.088(,B).27 G 1.587(ayer and Unterauer) +177.636 225.6 R 4.087(,A)-.4 G 1.587(CM T)270.447 225.6 R 1.587 +(ransactions on Database Systems, V)-.35 F 1.587(ol. 2, 1 \(March 1977\),)-1.29 +F(11-26.)108 237.6 Q F2(The Art of Computer Pr)108 261.6 Q -.1(og)-.45 G -.15 +(ra).1 G(mming V).15 E(ol. 3: Sorting and Sear)-1.11 E -.15(ch)-.37 G(ing).15 E +F0 2.5(,D).22 G(.E. Knuth, 1968, pp 471-480.)382 261.6 Q(4.4 Berk)72 732 Q(ele) +-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E +(3)535 732 Q EP +%%Page: 1 4 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_HASH\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_HASH\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 +84 S(ME).18 E F0(db_hash \255 hash database access method)108 96 Q F1 +(DESCRIPTION)72 112.8 Q F0 .485(The DB library is a f)108 124.8 R .485 +(amily of groups of functions that pro)-.1 F .486 +(vides a modular programming interf)-.15 F .486(ace to trans-)-.1 F .823 +(actions and record-oriented \214le access.)108 136.8 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 148.8 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 148.8 S .258(ell as v)237.082 148.8 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 148.8 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 148.8 S .258 +(he functional groups \(e.g.)436.746 148.8 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 160.8 R .306(tional groups are e)108 172.8 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 172.8 Q .245(description of transactions, see)108 184.8 R/F2 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F2(db_open)2.745 E +F0(\(3\)).24 E .307(and then the indi)108 196.8 R .307 +(vidual access method manual pages:)-.25 F F2(db_btr)2.808 E(ee)-.37 E F0 +(\(3\),).18 E F2(db_hash)2.808 E F0(\(3\),).28 E F2(db_lo)2.808 E(g)-.1 E F0 +.308(\(3\) and).22 F F2(db_r)2.808 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +208.8 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +208.8 R 3.635(,s)-.4 G(ee)307.32 208.8 Q F2(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 220.8 Q 2.5(,s)-.4 G(ee)171.2 220.8 Q F2(db_mpool)2.5 E F0 +(\(3\).).51 E +(This manual page describes speci\214c details of the hashing access method.) +108 237.6 Q .59(The hash data structure is an e)108 254.4 R .591 +(xtensible, dynamic hashing scheme.)-.15 F(Backw)5.591 E .591 +(ard compatible interf)-.1 F .591(aces to the)-.1 F .209 +(functions described in)108 266.4 R F2(dbm)2.709 E F0 .209(\(3\), and).32 F F2 +(ndbm)2.709 E F0 .209(\(3\) are pro).32 F .209(vided, ho)-.15 F(we)-.25 E -.15 +(ve)-.25 G 2.708(rt).15 G .208(hese interf)382.71 266.4 R .208 +(aces are not compatible with)-.1 F(pre)108 278.4 Q(vious \214le formats.)-.25 +E F1 -.495(AC)72 295.2 S(CESS METHOD SPECIFIC INFORMA).495 E(TION)-.855 E F0 +.612(The hash access method speci\214c data structure pro)108 307.2 R .612 +(vided to)-.15 F F2(db_open)3.112 E F0 .612(is typedef)3.112 F 1.612 -.5('d a) +.55 H .613(nd named HASHINFO.).5 F 2.5(AH)108 319.2 S +(ASHINFO structure has at least the follo)124.94 319.2 Q +(wing \214elds, which may be initialized before calling)-.25 E F2(db_open)2.5 E +F0(:).24 E(u_int bsize;)108 336 Q F2(Bsize)133 348 Q F0 2.041 +(de\214nes the hash table b)4.541 F(uck)-.2 E 2.041(et size, and is, by def)-.1 +F 2.04(ault, 256 bytes.)-.1 F 2.04(It may be preferable to)7.04 F +(increase the page size for disk-resident tables and tables with lar)133 360 Q +(ge data items.)-.18 E(u_int cachesize;)108 376.8 Q 3.846(As)133 388.8 S 1.347 +(uggested maximum size, in bytes, of the memory cache.)147.956 388.8 R 1.347 +(This v)6.347 F 1.347(alue is)-.25 F/F3 10/Times-Bold@0 SF(only)3.847 E F0 +(advisory)3.847 E 3.847(,a)-.65 G 1.347(nd the)513.933 388.8 R +(access method will allocate more memory rather than f)133 400.8 Q(ail.)-.1 E +(u_int f)108 417.6 Q -.1(fa)-.25 G(ctor;).1 E F2(Ffactor)133 429.6 Q F0 1.17 +(indicates a desired density within the hash table.)3.67 F 1.169 +(It is an approximation of the number of)6.169 F -.1(ke)133 441.6 S 1.162 +(ys allo)-.05 F 1.162(wed to accumulate in an)-.25 F 3.662(yo)-.15 G 1.162 +(ne b)284.852 441.6 R(uck)-.2 E 1.162(et, determining when the hash table gro) +-.1 F 1.162(ws or shrinks.)-.25 F(The def)133 453.6 Q(ault v)-.1 E(alue is 8.) +-.25 E(u_int32_t \(*hash\)\(const v)108 470.4 Q(oid *, size_t\);)-.2 E F2(Hash) +133 482.4 Q F0 .788(is a user de\214ned hash function.)3.288 F .787 +(Since no hash function performs equally well on all possible)5.788 F .017 +(data, the user may \214nd that the b)133 494.4 R .018 +(uilt-in hash function does poorly on a particular data set.)-.2 F .018 +(User speci-)5.018 F 1.154(\214ed hash functions must tak)133 506.4 R 3.654(et) +-.1 G 1.354 -.1(wo a)260.61 506.4 T -.18(rg).1 G 1.154 +(uments \(a pointer to a byte string and a length\) and return a).18 F +(32-bit quantity to be used as the hash v)133 518.4 Q(alue.)-.25 E .665 +(If a hash function is speci\214ed,)133 535.2 R F2(hash_open)3.165 E F0 .666 +(will attempt to determine if the hash function speci\214ed is)3.166 F +(the same as the one with which the database w)133 547.2 Q +(as created, and will f)-.1 E(ail if it is not.)-.1 E(int lorder;)108 564 Q .65 +(The byte order for inte)133 576 R .65(gers in the stored database metadata.) +-.15 F .65(The number should represent the order)5.65 F .748(as an inte)133 588 +R .749(ger; for e)-.15 F .749(xample, big endian order w)-.15 F .749 +(ould be the number 4,321.)-.1 F(If)5.749 E F2(lor)3.249 E(der)-.37 E F0 .749 +(is 0 \(no order is)3.249 F .456(speci\214ed\) the current host order is used.) +133 600 R .456(If the)5.456 F .456(\214le already e)5.456 F .456 +(xists, the speci\214ed v)-.15 F .455(alue is ignored and)-.25 F(the v)133 612 +Q(alue speci\214ed when the tree w)-.25 E(as created is used.)-.1 E +(u_int nelem;)108 628.8 Q F2(Nelem)133 640.8 Q F0 1.225 +(is an estimate of the \214nal size of the hash table.)3.724 F 1.225 +(If not set or set too lo)6.225 F 2.525 -.65(w, h)-.25 H 1.225(ash tables will) +.65 F -.15(ex)133 652.8 S 1.294(pand gracefully as k).15 F -.15(ey)-.1 G 3.794 +(sa).15 G 1.294(re entered, although a slight performance de)248.296 652.8 R +1.293(gradation may be noticed.)-.15 F(The def)133 664.8 Q(ault v)-.1 E +(alue is 1.)-.25 E .79(If the \214le already e)108 681.6 R .79 +(xists \(and the O_TR)-.15 F .79(UNC \215ag is not speci\214ed\), the v)-.4 F +.79(alues speci\214ed for the parameters)-.25 F(bsize, f)108 693.6 Q -.1(fa) +-.25 G(ctor).1 E 2.5(,l)-.4 G(order and nelem are ignored and the v)167.23 +693.6 Q(alues speci\214ed when the tree w)-.25 E(as created are used.)-.1 E +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 5 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_HASH\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_HASH\(3\))340.17 48 R/F1 9/Times-Bold@0 SF(DB OPERA) +72 84 Q(TIONS)-.855 E F0(The functions returned by)108 96 Q/F2 10 +/Times-Italic@0 SF(db_open)2.5 E F0 +(for the hash access method are as described in)2.5 E F2(db_open)2.5 E F0 +(\(3\).).24 E F1(ERR)72 112.8 Q(ORS)-.27 E F0(The)108 124.8 Q F2(hash)2.609 E +F0 .109(access method functions may f)2.609 F .109(ail and set)-.1 F F2(errno) +2.609 E F0 .109(for an)2.609 F 2.609(yo)-.15 G 2.609(ft)375.678 124.8 S .109 +(he errors speci\214ed for the library func-)384.397 124.8 R(tion)108 136.8 Q +F2(db_open)2.5 E F0(\(3\).).24 E F1(SEE ALSO)72 153.6 Q F2(db_btr)108 165.6 Q +(ee)-.37 E F0(\(3\),).18 E F2(db_loc)2.5 E(k)-.2 E F0(\(3\),).67 E F2(db_lo)2.5 +E(g)-.1 E F0(\(3\),).22 E F2(db_mpool)2.5 E F0(\(3\),).51 E F2(db_open)2.5 E F0 +(\(3\),).24 E F2(db_r)2.5 E(ecno)-.37 E F0(\(3\),).18 E F2(db_txn)2.5 E F0 +(\(3\)).24 E F2(Dynamic Hash T)108 189.6 Q(ables)-.92 E F0 2.5(,P).27 G(er) +206.79 189.6 Q(-Ak)-.2 E 2.5(eL)-.1 G(arson, Communications of the A)242.86 +189.6 Q(CM, April 1988.)-.4 E F2 2.5(AN)108 213.6 S .3 -.15(ew H)123.28 213.6 T +(ash P).15 E(ac)-.8 E(ka)-.2 E .2 -.1(ge f)-.1 H(or UNIX).1 E F0 2.5(,M).94 G +(ar)248.41 213.6 Q(go Seltzer)-.18 E 2.5(,U)-.4 G(SENIX Proceedings, W)308.09 +213.6 Q(inter 1991.)-.4 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib) +132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(2)535 732 Q EP +%%Page: 1 6 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_LOCK\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_LOCK\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 +84 S(ME).18 E F0(db_lock \255 general purpose lock manager)108 96 Q F1 +(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(#include )108 124.8 Q +(int)108 148.8 Q(lock_cr)108 160.8 Q(eate\(const char *path, mode_t mode,)-.18 +E(int lock_modes, const int8_t con\215icts[][], u_int maxlocks\);)158 172.8 Q +(LOCK_T)108 196.8 Q(ABLE_T *)-.9 E(lock_open\(const char *path\);)108 208.8 Q +(int)108 232.8 Q(lock_v)108 244.8 Q(ec\(LOCK_T)-.1 E(ABLE_T *lt, DBT *lock)-.9 +E(er)-.1 E 2.5(,s)-.92 G(truct timespec *timeout,)308.21 244.8 Q +(LOCK_REQ_T list[], int nlist, LOCK_REQ_T **elistp, DBT *con\215ict\);)158 +256.8 Q(int)108 280.8 Q(lock_get\(LOCK_T)108 292.8 Q +(ABLE_T *lt, const DBT *lock)-.9 E(er)-.1 E(,)-.92 E +(const DBT *obj, const lock_mode_t lock_mode, LOCK_T **lockp\);)158 304.8 Q +(int)108 328.8 Q(lock_put\(LOCK_T *lockp\);)108 340.8 Q(int)108 364.8 Q +(lock_close\(LOCK_T)108 376.8 Q(ABLE_T *lt\);)-.9 E(int)108 400.8 Q +(lock_unlink\(const char *path, int f)108 412.8 Q(or)-.25 E(ce\);)-.18 E F1 +(DESCRIPTION)72 429.6 Q F0 .485(The DB library is a f)108 441.6 R .485 +(amily of groups of functions that pro)-.1 F .486 +(vides a modular programming interf)-.15 F .486(ace to trans-)-.1 F .823 +(actions and record-oriented \214le access.)108 453.6 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 465.6 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 465.6 S .258(ell as v)237.082 465.6 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 465.6 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 465.6 S .258 +(he functional groups \(e.g.)436.746 465.6 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 477.6 R .306(tional groups are e)108 489.6 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 489.6 Q .245(description of transactions, see)108 501.6 R/F3 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F3(db_open)2.745 E +F0(\(3\)).24 E .307(and then the indi)108 513.6 R .307 +(vidual access method manual pages:)-.25 F F3(db_btr)2.808 E(ee)-.37 E F0 +(\(3\),).18 E F3(db_hash)2.808 E F0(\(3\),).28 E F3(db_lo)2.808 E(g)-.1 E F0 +.308(\(3\) and).22 F F3(db_r)2.808 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +525.6 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +525.6 R 3.635(,s)-.4 G(ee)307.32 525.6 Q F3(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 537.6 Q 2.5(,s)-.4 G(ee)171.2 537.6 Q F3(db_mpool)2.5 E F0 +(\(3\).).51 E +(This manual page describes speci\214c details of the locking interf)108 554.4 +Q(ace.)-.1 E F3(Db_loc)108 571.2 Q(k)-.2 E F0 .346(is the library interf)2.846 +F .346(ace intended to pro)-.1 F .346(vide general-purpose locking.)-.15 F .347 +(While designed to w)5.347 F .347(ork with)-.1 F .946(the other DB functions, \ +these functions are also useful for more general locking purposes.)108 583.2 R +.946(Locks can be)5.946 F(shared between processes.)108 595.2 Q .682 +(The function)108 612 R F3(loc)3.182 E(k_cr)-.2 E(eate)-.37 E F0 .683 +(creates and initializes the lock table identi\214ed by the)3.182 F F3(path) +3.183 E F0(directory)3.183 E 5.683(.T)-.65 G .683(his direc-)501.827 612 R .565 +(tory must already e)108 624 R .565(xist when)-.15 F F3(loc)3.065 E(k_cr)-.2 E +(eate)-.37 E F0 .565(is called.)3.065 F .565(If the lock table identi\214ed by) +5.565 F F3(path)3.064 E F0 .564(already e)3.064 F .564(xists, then)-.15 F F3 +(loc)108 636 Q(k_cr)-.2 E(eate)-.37 E F0 .974 +(returns success without further action.)3.474 F .974 +(The \214les associated with the lock table are created in)5.974 F 2.017 +(the directory speci\214ed by)108 648 R F3(path)4.517 E F0 7.017(.\().28 G +2.017(The group of the created \214les is based on the system and directory) +250.846 648 R(def)108 660 Q .076(aults, and is not further speci\214ed by)-.1 F +F3(loc)2.576 E(k_cr)-.2 E(eate)-.37 E F0 2.576(.\) All).18 F .076 +(\214les created by)2.576 F F3(loc)2.576 E(k_cr)-.2 E(eate)-.37 E F0 .077 +(are created with mode)2.577 F F3(mode)108 672 Q F0(\(as described in)2.5 E F3 +-.15(ch)2.5 G(mod).15 E F0(\(2\)\) and modi\214ed by the process' umask v).77 E +(alue \(see)-.25 E F3(umask)2.5 E F0(\(2\)\).).67 E .739(The parameter)108 +688.8 R F3(loc)3.239 E(k_modes)-.2 E F0 .739(is the number of lock modes to be\ + recognized by the lock table \(including the)3.239 F(4.4 Berk)72 732 Q(ele)-.1 +E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(1) +535 732 Q EP +%%Page: 2 7 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_LOCK\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_LOCK\(3\))340.17 48 R -.74(``)108 84 S(not-granted') +.74 E 2.5('m)-.74 G 2.5(ode\). The)176.22 84 R(parameter)2.5 E/F1 10 +/Times-Italic@0 SF(con\215icts)2.5 E F0(is an)2.5 E F1(loc)2.5 E(k_modes)-.2 E +F0(by)2.5 E F1(loc)2.5 E(k_modes)-.2 E F0(array)2.5 E 5(.A)-.65 G(non-0 v) +467.59 84 Q(alue for:)-.25 E(con\215icts[requested_mode][held_mode])158 108 Q +.174(indicates that)108 132 R F1 -.37(re)2.674 G(quested_mode).37 E F0(and) +2.674 E F1(held_mode)2.674 E F0 2.675(con\215ict. The)2.674 F -.74(``)2.675 G +(not-granted').74 E 2.675('m)-.74 G .175(ode must be represented by 0.)419.705 +132 R(The include \214le declares tw)108 148.8 Q 2.5(oc)-.1 G +(ommonly used con\215ict arrays:)283.87 148.8 Q(int lock_sx_n;)108 165.6 Q +(const int8_t lock_sx_c[lock_sx_n][lock_sx_n];)108 177.6 Q(These v)133 189.6 Q +(ariables specify a con\215ict array for a simple scheme using shared and e) +-.25 E(xclusi)-.15 E .3 -.15(ve l)-.25 H(ock modes.).15 E(int lock_g_n;)108 +206.4 Q(const int8_t lock_g_c[lock_g_n][lock_g_n];)108 218.4 Q 1.071(These v) +133 230.4 R 1.071(ariables specify a con\215ict array that in)-.25 F -.2(vo)-.4 +G(lv).2 E 1.071(es v)-.15 F 1.07 +(arious intent lock modes \(e.g. intent shared\))-.25 F +(that are used for multigranularity locking.)133 242.4 Q 1.53 +(In addition, de\214nes the follo)108 259.2 R 1.531 +(wing macros that name lock modes for use with the standard)-.25 F(tables abo) +108 271.2 Q -.15(ve)-.15 G(:).15 E(LOCK_IS)144 288 Q(intent shared)169 300 Q +(LOCK_IX)144 312 Q(intent e)169 324 Q(xclusi)-.15 E -.15(ve)-.25 G(LOCK_NG)144 +336 Q(not granted \(al)169 348 Q -.1(wa)-.1 G(ys 0\)).1 E(LOCK_S)144 360 Q +(shared)169 372 Q(LOCK_SIX)144 384 Q(shared/intent e)169 396 Q(xclusi)-.15 E +-.15(ve)-.25 G(LOCK_X)144 408 Q -.15(ex)169 420 S(clusi).15 E -.15(ve)-.25 G F1 +(Maxloc)108 436.8 Q(ks)-.2 E F0 .442(is the maximum number of locks to be held\ + or requested in the table, and is used by)2.942 F F1(loc)2.941 E(k_cr)-.2 E +(eate)-.37 E F0(to estimate ho)108 448.8 Q 2.5(wm)-.25 G +(uch space to allocate for v)181.36 448.8 Q(arious lock-table data structures.) +-.25 E(The function)108 465.6 Q F1(loc)2.5 E(k_cr)-.2 E(eate)-.37 E F0 +(returns -1 on f)2.5 E(ailure, setting)-.1 E F1(errno)2.5 E F0 2.5(,a).18 G +(nd 0 on success.)356.07 465.6 Q .202(The function)108 482.4 R F1(loc)2.703 E +(k_open)-.2 E F0 .203(returns a pointer to the lock table identi\214ed by)2.703 +F F1(path)2.703 E F0 2.703(,w).28 G .203(hich must ha)425.678 482.4 R .503 -.15 +(ve a)-.2 H .203(lready been).15 F 1.162(created by a call to)108 494.4 R F1 +(loc)3.661 E(k_cr)-.2 E(eate)-.37 E F0 6.161(.T).18 G 1.161(he process must ha) +252.869 494.4 R 1.461 -.15(ve p)-.2 H 1.161 +(ermission to read and write \214les with o).15 F(wners,)-.25 E .06 +(groups and permissions as described for)108 506.4 R F1(loc)2.56 E(k_cr)-.2 E +(eate)-.37 E F0 5.06(.T).18 G(he)331.04 506.4 Q F1(loc)2.56 E(k_open)-.2 E F0 +.06(function returns NULL on f)2.56 F .06(ailure, set-)-.1 F(ting)108 518.4 Q +F1(errno)2.5 E F0(.).18 E .986(The function)108 535.2 R F1(loc)3.486 E(k_vec) +-.2 E F0 .986 +(atomically obtains and releases one or more locks from the designated table.) +3.486 F(The)5.986 E(function)108 547.2 Q F1(loc)4.52 E(k_vec)-.2 E F0 2.02(is \ +intended to support acquisition or trading of multiple locks under one lock ta\ +ble)4.52 F(semaphore, as is needed for lock coupling or in multigranularity lo\ +cking for lock escalation.)108 559.2 Q .746(If an)108 576 R 3.246(yo)-.15 G +3.246(ft)140.442 576 S .746(he requested locks cannot be acquired or an)149.798 +576 R 3.246(yo)-.15 G 3.246(ft)342.786 576 S .746 +(he locks to be released cannot be released, no)352.142 576 R .117 +(locks are acquired and no locks are released, and)108 588 R F1(loc)2.617 E +(k_vec)-.2 E F0 .117(returns an error)2.617 F 5.117(.T)-.55 G .117(he function) +419.211 588 R F1(loc)2.617 E(k_vec)-.2 E F0 .118(returns 0)2.617 F 1.143 +(on success.)108 600 R 1.143(If an error occurs,)6.143 F F1(loc)3.642 E(k_vec) +-.2 E F0 1.142(returns one of the follo)3.642 F 1.142(wing v)-.25 F 3.642 +(alues. In)-.25 F 1.142(addition, if)3.642 F F1(elistp)3.642 E F0 1.142(is not) +3.642 F(NULL, it is set to point to the LOCK_REQ_T entry which w)108 612 Q +(as being processed when the error occurred.)-.1 E(LOCK_GET_DEADLOCK)108 628.8 +Q .431(The speci\214ed)133 640.8 R F1(loc)2.931 E -.1(ke)-.2 G(r).1 E F0 -.1 +(wa)2.931 G 2.931(ss).1 G .431(elected as a victim in order to resolv)239.854 +640.8 R 2.932(ead)-.15 G 2.932(eadlock. In)407.718 640.8 R .432 +(this case, if the)2.932 F F1(con-)2.932 E(\215ict)133 652.8 Q F0(ar)2.901 E +.401(gument is non-NULL, it is set to reference the identity of a lock)-.18 F +.4(er holding the lock referenced)-.1 F(by)133 664.8 Q F1(elistp)2.585 E F0 +.085(at the time the request w)2.585 F .085(as denied.)-.1 F .086 +(\(This identity resides in static memory and may be o)5.086 F -.15(ve)-.15 G +-.2(r-).15 G(written by subsequent calls to)133 676.8 Q F1(loc)2.5 E(k_vec)-.2 +E F0(\).).31 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q +99.315(ution August)-.2 F(1, 1995)2.5 E(2)535 732 Q EP +%%Page: 3 8 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_LOCK\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_LOCK\(3\))340.17 48 R(LOCK_GET_ERR)108 84 Q(OR)-.4 E +(An error occurred and the e)133 96 Q(xternal v)-.15 E(ariable)-.25 E/F1 10 +/Times-Italic@0 SF(errno)2.5 E F0(has been set to indicate the error)2.5 E(.) +-.55 E(LOCK_GET_NO)108 112.8 Q(THELD)-.4 E +(The lock cannot be released, as it w)133 124.8 Q(as not held by the)-.1 E F1 +(loc)2.5 E -.1(ke)-.2 G(r).1 E F0(.).73 E(LOCK_GET_RESOURCE)108 141.6 Q 2.311(\ +The lock manager is unable to grant the requested locks because of limited int\ +ernal resources.)133 153.6 R(\(Releasing locks may allo)133 165.6 Q 2.5(wf)-.25 +G(uture calls to)249.4 165.6 Q F1(loc)2.5 E(k_vec)-.2 E F0(to succeed.\))2.5 E +(LOCK_GET_TIMEOUT)108 182.4 Q 3.204(At)133 194.4 S .704(imeout ar)146.204 194.4 +R .704(gument w)-.18 F .705(as speci\214ed, and the requested locks were not a) +-.1 F -.25(va)-.2 G .705(ilable soon enough.).25 F .705(In this)5.705 F .625 +(case, if the)133 206.4 R F1(con\215ict)3.125 E F0(ar)3.125 E .624 +(gument is non-NULL, it is set to reference the identity of a lock)-.18 F .624 +(er holding the)-.1 F .551(lock referenced by)133 218.4 R F1(elistp)3.052 E F0 +.552(at the time the request w)3.052 F .552(as denied.)-.1 F .552 +(\(This identity resides in static memory)5.552 F(and may be o)133 230.4 Q -.15 +(ve)-.15 G(rwritten by subsequent calls to).15 E F1(loc)2.5 E(k_vec)-.2 E F0 +(\).).31 E(The)108 247.2 Q F1(loc)3.005 E -.1(ke)-.2 G(r).1 E F0(ar)3.005 E +.504(gument speci\214ed to)-.18 F F1(loc)3.004 E(k_vec)-.2 E F0 .504 +(is a pointer to an untyped byte string which identi\214es the entity)3.004 F +(requesting or releasing the lock.)108 259.2 Q(If)5 E F1(loc)2.5 E -.1(ke)-.2 G +(r).1 E F0(is NULL, the calling process' pid is used instead.)2.5 E(The)108 276 +Q F1(timeout)4.628 E F0(ar)4.628 E 2.128(gument pro)-.18 F 2.128(vided to)-.15 +F F1(loc)4.628 E(k_vec)-.2 E F0 2.128(speci\214es a maximum interv)4.628 F +2.128(al to w)-.25 F 2.128(ait for the locks to be)-.1 F 2.642(granted. If)108 +288 R F1(timeout)2.642 E F0 .142(is NULL, it is ignored, and)2.642 F F1(loc) +2.642 E(k_vec)-.2 E F0 .141 +(will not return until all of the locks are acquired or)2.642 F +(an error has occurred.)108 300 Q(The)108 316.8 Q F1(list)4.263 E F0 1.764 +(array pro)4.263 F 1.764(vided to)-.15 F F1(loc)4.264 E(k_vec)-.2 E F0 1.764 +(is typedef)4.264 F 2.764 -.5('d i).55 H 4.264(n<).5 G 1.764 +(db_lock.h> as LOCK_REQ_T)331.114 316.8 R 6.764(.A)-.74 G(LOCK_REQ_T)476.67 +316.8 Q(structure has at least the follo)108 328.8 Q +(wing \214elds, which must be initialized before calling)-.25 E F1(loc)2.5 E +(k_vec)-.2 E F0(:).31 E(enum lock)108 345.6 Q(op op;)-.1 E +(The operation to be performed, which must be set to one of the follo)133 357.6 +Q(wing v)-.25 E(alues:)-.25 E(LOCK_GET)133 374.4 Q .201 +(Get a lock, as de\214ned by the v)158 386.4 R .201(alues of)-.25 F F1(loc) +2.701 E -.1(ke)-.2 G(r).1 E F0(,).73 E F1(obj)2.701 E F0(and)2.7 E F1(loc)2.7 E +(k_mode)-.2 E F0 5.2(.U).18 G .2(pon return from)435.99 386.4 R F1(loc)2.7 E +(k_vec)-.2 E F0(,).31 E .161(if the)158 398.4 R F1(loc)2.661 E(kp)-.2 E F0 .162 +(\214eld is non-NULL, a reference to the acquired lock is stored there.)2.662 F +.162(\(This reference)5.162 F(is in)158 410.4 Q -.25(va)-.4 G(lidated by an).25 +E 2.5(yc)-.15 G(all to)247.19 410.4 Q F1(loc)2.5 E(k_vec)-.2 E F0(or)2.5 E F1 +(loc)2.5 E(k_put)-.2 E F0(which releases the lock.\))2.5 E(LOCK_PUT)133 427.2 Q +(The lock referenced by the contents of the)158 439.2 Q F1(loc)2.5 E(kp)-.2 E +F0(\214eld is released.)2.5 E(LOCK_PUT_ALL)133 456 Q .759 +(All locks held by the)158 468 R F1(loc)3.259 E -.1(ke)-.2 G(r).1 E F0 .759 +(are released.)3.259 F(\(An)5.759 E 3.259(yl)-.15 G .759 +(ocks acquired as a part of the current call to)358.501 468 R F1(loc)158 480 Q +(k_vec)-.2 E F0(are not considered for this operation\).)2.5 E(LOCK_PUT_OBJ)133 +496.8 Q 1.409(All locks held by the)158 508.8 R F1(loc)3.909 E -.1(ke)-.2 G(r) +.1 E F0 3.909(,o).73 G 3.909(nt)287.704 508.8 S 1.409(he object)299.393 508.8 R +F1(obj)3.909 E F0 3.909(,w).48 G 1.41(ith the mode speci\214ed by)367.98 508.8 +R F1(loc)3.91 E(k_mode)-.2 E F0 3.91(,a).18 G(re)532.23 508.8 Q 2.802 +(released. A)158 520.8 R F1(loc)2.802 E(k_mode)-.2 E F0 .301 +(of LOCK_NG indicates that all locks on the object should be released.)2.802 F +(\(An)158 532.8 Q 3.053(yl)-.15 G .553 +(ocks acquired as a part of the current call to)184.233 532.8 R F1(loc)3.054 E +(k_vec)-.2 E F0 .554(are not considered for this opera-)3.054 F(tion\).)158 +544.8 Q(const DBT obj;)108 561.6 Q +(An untyped byte string which speci\214es the object to be lock)133 573.6 Q +(ed or released.)-.1 E(const lock_mode_t lock_mode;)108 590.4 Q +(The lock mode, used as an inde)133 602.4 Q 2.5(xi)-.15 G(nto)268.94 602.4 Q F1 +(lt)2.5 E F0 1.1 -.55('s c).68 H(on\215ict array).55 E(.)-.65 E +(LOCK_T **lockp;)108 619.2 Q 2.5(Ap)133 631.2 S +(ointer to a pointer to a lock reference.)147.72 631.2 Q(The)108 648 Q F1 +(nlist)2.5 E F0(ar)2.5 E(gument speci\214es the number of elements in the)-.18 +E F1(list)2.5 E F0(array)2.5 E(.)-.65 E 1.229(The function)108 664.8 R F1(loc) +3.729 E(k_g)-.2 E(et)-.1 E F0 1.228(is a simple interf)3.728 F 1.228 +(ace to the)-.1 F F1(loc)3.728 E(k_vec)-.2 E F0(functionality)3.728 E 3.728(,a) +-.65 G 1.228(nd is equi)416.31 664.8 R -.25(va)-.25 G 1.228 +(lent to calling the).25 F F1(loc)108 676.8 Q(k_vec)-.2 E F0 .123 +(function with the)2.623 F F1(lt)2.623 E F0(and)2.623 E F1(loc)2.623 E -.1(ke) +-.2 G(r).1 E F0(ar)2.623 E .123(guments, NULL)-.18 F F1(timeout)2.623 E F0(,) +.68 E F1(elistp)2.623 E F0(and)2.623 E F1(con\215ict)2.623 E F0(ar)2.623 E .124 +(guments, and a sin-)-.18 F .944(gle element)108 688.8 R F1(list)3.444 E F0 +(array)3.444 E 3.444(,f)-.65 G .944(or which the)203.606 688.8 R F1(op)3.444 E +F0 .944(\214eld is LOCK_GET)3.444 F 3.444(,a)-.74 G .944(nd the)365.014 688.8 R +F1(obj)3.444 E F0(,).48 E F1(loc)3.444 E(k_mode)-.2 E F0(and)3.444 E F1(loc) +3.444 E(kp)-.2 E F0 .943(\214elds are)3.443 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5 +(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(3)535 +732 Q EP +%%Page: 4 9 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 117.9(DB_LOCK\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 117.9(anual DB_LOCK\(3\))340.17 48 R .509(represented by the ar)108 +84 R .509(guments of the same name.)-.18 F .51(Note that the type of the)5.509 +F/F1 10/Times-Italic@0 SF(obj)3.01 E F0(ar)3.01 E .51(gument to)-.18 F F1(loc) +3.01 E(k_g)-.2 E(et)-.1 E F0 .51(is dif-)3.01 F .765(ferent from the)108 96 R +F1(obj)3.265 E F0 .765(element found in the LOCK_REQ_T structure.)3.265 F(The) +5.765 E F1(loc)3.265 E(k_g)-.2 E(et)-.1 E F0 .765(function returns success) +3.265 F(and f)108 108 Q(ailure as described for the)-.1 E F1(loc)2.5 E(k_vec) +-.2 E F0(function.)2.5 E 1.186(The function)108 124.8 R F1(loc)3.686 E(k_put) +-.2 E F0 1.187(is a simple interf)3.687 F 1.187(ace to the)-.1 F F1(loc)3.687 E +(k_vec)-.2 E F0(functionality)3.687 E 3.687(,a)-.65 G 1.187(nd is equi)416.515 +124.8 R -.25(va)-.25 G 1.187(lent to calling the).25 F F1(loc)108 136.8 Q +(k_vec)-.2 E F0 .374(function with a single element)2.874 F F1(list)2.874 E F0 +(array)2.873 E 2.873(,f)-.65 G .373(or which the)314.82 136.8 R F1(op)2.873 E +F0 .373(\214eld is LOCK_PUT and the)2.873 F F1(loc)2.873 E(kp)-.2 E F0(\214eld) +2.873 E .631(is represented by the ar)108 148.8 R .631 +(gument of the same name.)-.18 F .632(Note that the type of the)5.632 F F1(loc) +3.132 E(kp)-.2 E F0(ar)3.132 E .632(gument to)-.18 F F1(loc)3.132 E(k_put)-.2 E +F0(is)3.132 E(dif)108 160.8 Q .275(ferent from the)-.25 F F1(loc)2.775 E(kp)-.2 +E F0 .274(element found in the LOCK_REQ_T structure.)2.775 F(The)5.274 E F1 +(loc)2.774 E(k_put)-.2 E F0 .274(function returns suc-)2.774 F(cess and f)108 +172.8 Q(ailure as described for the)-.1 E F1(loc)2.5 E(k_vec)-.2 E F0 +(function.)2.5 E .013(The function)108 189.6 R F1(loc)2.513 E(k_close)-.2 E F0 +.013(disassociates the calling process from the lock table)2.513 F F1(lt)2.513 +E F0 2.513(,a).68 G .013(fter releasing all locks held)431.636 189.6 R .228 +(or requested by that process.)108 201.6 R .228(The function)5.228 F F1(loc) +2.728 E(k_close)-.2 E F0 .228(returns -1 on f)2.728 F .227(ailure, setting)-.1 +F F1(errno)2.727 E F0 2.727(,a).18 G .227(nd 0 on success.)474.329 201.6 R .433 +(The function)108 218.4 R F1(loc)2.933 E(k_unlink)-.2 E F0(destro)2.933 E .433 +(ys the lock table identi\214ed by the directory)-.1 F F1(path)2.933 E F0 2.933 +(,r).28 G(emo)440.636 218.4 Q .433(ving all \214les used to)-.15 F 1.005 +(implement the lock table.)108 230.4 R 1.005(\(The directory)6.005 F F1(path) +3.505 E F0 1.005(is not remo)3.505 F -.15(ve)-.15 G 3.505(d.\) If).15 F 1.005 +(there are processes which ha)3.505 F 1.305 -.15(ve c)-.2 H(alled).15 E F1(loc) +108 242.4 Q(k_open)-.2 E F0 .869(without calling)3.369 F F1(loc)3.369 E +(k_close)-.2 E F0 .869 +(\(i.e., there are processes currently using the lock table\),)3.369 F F1(loc) +3.37 E(k_unlink)-.2 E F0 .409(will f)108 254.4 R .408 +(ail without further action, unless the force \215ag is set, in which case)-.1 +F F1(loc)2.908 E(k_unlink)-.2 E F0 .408(will attempt to delete)2.908 F .807 +(the lock table \214les re)108 266.4 R -.05(ga)-.15 G .808(rdless of an).05 F +3.308(yp)-.15 G .808(rocesses still using the lock table.)264.662 266.4 R(An) +5.808 E 3.308(ya)-.15 G .808(ccesses to a remo)433.208 266.4 R -.15(ve)-.15 G +3.308(dl).15 G(ock)525.56 266.4 Q .046(table will lik)108 278.4 R .046 +(ely result in une)-.1 F .045(xpected beha)-.15 F(vior)-.2 E 5.045(.T)-.55 G +.045(he function)304.24 278.4 R F1(loc)2.545 E(k_unlink)-.2 E F0 .045 +(returns -1 on f)2.545 F .045(ailure, setting)-.1 F F1(errno)2.545 E F0(,).18 E +(and 0 on success.)108 290.4 Q .798(In the case of catastrophic or system f)108 +307.2 R .798(ailure, it is possible to clean up a lock table by remo)-.1 F .799 +(ving all of the)-.15 F .38(\214les in the directory speci\214ed to the)108 +319.2 R F1(loc)2.88 E(k_cr)-.2 E(eate)-.37 E F0 .379 +(function, as lock table \214les are ne)2.88 F -.15(ve)-.25 G 2.879(rc).15 G +.379(reated in an)461.543 319.2 R 2.879(yd)-.15 G(irec-)521.68 319.2 Q +(tory other than the one speci\214ed to)108 331.2 Q F1(loc)2.5 E(k_cr)-.2 E +(eate)-.37 E F0(.).18 E/F2 9/Times-Bold@0 SF(ERR)72 348 Q(ORS)-.27 E F0(The)108 +360 Q F1(loc)4.158 E(k_cr)-.2 E(eate)-.37 E F0 1.658(function may f)4.158 F +1.658(ail and set)-.1 F F1(errno)4.158 E F0 1.658(for an)4.158 F 4.158(yo)-.15 +G 4.158(ft)353.71 360 S 1.659(he errors speci\214ed for the library routines) +363.978 360 R F1(mmap)108 372 Q F0(\(2\),).19 E F1(open)2.5 E F0(\(2\) and).24 +E F1(malloc)2.5 E F0(\(3\).).31 E(The)108 388.8 Q F1(loc)4.692 E(k_open)-.2 E +F0 2.192(function may f)4.692 F 2.192(ail and set)-.1 F F1(errno)4.692 E F0 +2.192(for an)4.692 F 4.692(yo)-.15 G 4.692(ft)353.87 388.8 S 2.191 +(he errors speci\214ed for the library routine)364.672 388.8 R F1(mmap)108 +400.8 Q F0(\(2\) and).19 E F1(open)2.5 E F0(\(2\).).24 E(The)108 417.6 Q F1 +(loc)2.57 E(k_close)-.2 E F0 .07(function may f)2.57 F .07(ail and set)-.1 F F1 +(errno)2.57 E F0 .07(for an)2.57 F 2.57(yo)-.15 G 2.57(ft)333.76 417.6 S .07 +(he errors speci\214ed for the library routine)342.44 417.6 R F1(close)2.57 E +F0(\(2\)).18 E(and)108 429.6 Q F1(munmap)2.5 E F0(\(2\).).19 E(The)108 446.4 Q +F1(loc)4.071 E(k_unlink)-.2 E F0 1.571(function may f)4.071 F 1.571 +(ail and set)-.1 F F1(errno)4.071 E F0 1.571(for an)4.071 F 4.071(yo)-.15 G +4.07(ft)353.22 446.4 S 1.57(he errors speci\214ed for the library function) +363.4 446.4 R F1(unlink)108 458.4 Q F0(\(2\) or the follo).67 E(wing:)-.25 E +([EB)108 475.2 Q(USY])-.1 E(The lock table w)133 487.2 Q +(as in use and the force \215ag w)-.1 E(as not set.)-.1 E F2(SEE ALSO)72 504 Q +F1(db_btr)108 516 Q(ee)-.37 E F0(\(3\),).18 E F1(db_hash)2.5 E F0(\(3\),).28 E +F1(db_lo)2.5 E(g)-.1 E F0(\(3\),).22 E F1(db_mpool)2.5 E F0(\(3\),).51 E F1 +(db_open)2.5 E F0(\(3\),).24 E F1(db_r)2.5 E(ecno)-.37 E F0(\(3\),).18 E F1 +(db_txn)2.5 E F0(\(3\)).24 E F2 -.09(BU)72 532.8 S(GS).09 E F0(The)108 544.8 Q +F1(maxloc)2.656 E(ks)-.2 E F0 .156 +(parameter is a kluge, and should be deleted in f)2.656 F -.2(avo)-.1 G 2.657 +(ro).2 G 2.657(fd)381.055 544.8 S .157(ynamically e)392.042 544.8 R .157 +(xpanding the lock table.)-.15 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G +(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(4)535 732 Q EP +%%Page: 1 10 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_LOG\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_LOG\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 +84 S(ME).18 E F0(db_log \255 log-manager access method)108 96 Q F1(DESCRIPTION) +72 112.8 Q F0 .486(The DB library is a f)108 124.8 R .485 +(amily of groups of functions that pro)-.1 F .485 +(vides a modular programming interf)-.15 F .485(ace to trans-)-.1 F .822 +(actions and record-oriented \214le access.)108 136.8 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 148.8 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 148.8 S .258(ell as v)237.082 148.8 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 148.8 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 148.8 S .258 +(he functional groups \(e.g.)436.746 148.8 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 160.8 R .306(tional groups are e)108 172.8 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 172.8 Q .245(description of transactions, see)108 184.8 R/F2 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F2(db_open)2.745 E +F0(\(3\)).24 E .308(and then the indi)108 196.8 R .308 +(vidual access method manual pages:)-.25 F F2(db_btr)2.807 E(ee)-.37 E F0 +(\(3\),).18 E F2(db_hash)2.807 E F0(\(3\),).28 E F2(db_lo)2.807 E(g)-.1 E F0 +.307(\(3\) and).22 F F2(db_r)2.807 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +208.8 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +208.8 R 3.635(,s)-.4 G(ee)307.32 208.8 Q F2(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 220.8 Q 2.5(,s)-.4 G(ee)171.2 220.8 Q F2(db_mpool)2.5 E F0 +(\(3\).).51 E +(This manual page describes speci\214c details of the logging access method.) +108 237.6 Q .03(These functions pro)108 254.4 R .03 +(vide a general-purpose logging f)-.15 F .03(acility suf)-.1 F .03 +(\214cient for transaction management.)-.25 F .03(Logs can)5.03 F +(be shared by multiple processes.)108 266.4 Q 3.717(Al)108 283.2 S 1.217 +(og is represented by the directory)121.717 283.2 R(,)-.65 E F2 1.217 +(not the \214le)3.717 F F0 3.717(,n).18 G 1.217(amed by the \214rst ar)323 +283.2 R 1.218(gument to)-.18 F F2(db_open)3.718 E F0 3.718(\(3\). The).24 F +(\214rst)3.718 E(ar)108 295.2 Q .26 +(gument must be non-NULL, and the directory must already e)-.18 F(xist)-.15 E +F2(db_open)2.76 E F0 .26(is called.)2.76 F .26(In that directory)5.26 F 2.76 +(,t)-.65 G(he)530.56 295.2 Q 3.448 +(log is stored in one or more \214les named in the format `)108 307.2 R +(`log.YYYY)-.74 E(.MM.DD.HH.MM.SS')-1.29 E 3.448(', where)-.74 F -.74(``)108 +319.2 S(YYYY).74 E(.MM.DD.HH.SS')-1.29 E 2.507('i)-.74 G 2.507(st)220.497 319.2 +S .007(he approximate creation time of the log \214le, and is guaranteed to be\ + unique in)229.674 319.2 R(the directory)108 331.2 Q(.)-.65 E .465 +(The group of the created \214les is based on the system and directory def)108 +348 R .466(aults, and is not further speci\214ed by)-.1 F .073 +(the log access method.)108 360 R .072(All \214les are created with the)5.073 F +F2(mode)2.572 E F0 .072(speci\214ed to)2.572 F F2(db_open)2.572 E F0 2.572(,\() +.24 G .072(as described in)435.584 360 R F2 -.15(ch)2.572 G(mod).15 E F0 +(\(2\)\)).77 E(and modi\214ed by the process' umask v)108 372 Q(alue \(see)-.25 +E F2(umask)2.5 E F0(\(2\)\).).67 E(The)108 388.8 Q F2<8d61>2.5 E(gs)-.1 E F0 +(ar)2.5 E(gument to)-.18 E F2(db_open)2.5 E F0(must be 0 for the)2.5 E F2 +(db_lo)2.5 E(g)-.1 E F0(access method.)2.5 E F1 -.495(AC)72 405.6 S +(CESS METHOD SPECIFIC INFORMA).495 E(TION)-.855 E F0 .571 +(The log access method speci\214c data structure pro)108 417.6 R .571(vided to) +-.15 F F2(db_open)3.071 E F0 .572(is typedef)3.071 F 1.572 -.5('d a).55 H .572 +(nd named LOGINFO.).5 F(A)5.572 E(LOGINFO structure has at least the follo)108 +429.6 Q(wing \214elds, which may be initialized before calling)-.25 E F2 +(db_open)2.5 E F0(:).24 E(of)108 446.4 Q(f_t max_\214le_size;)-.25 E 1.585 +(The maximum size of a single \214le in the log.)133 458.4 R 1.584 +(If not speci\214ed, the maximum size def)6.584 F 1.584(aults to an)-.1 F +(implementation-speci\214c v)133 470.4 Q(alue.)-.25 E(int lorder;)108 487.2 Q +.65(The byte order for inte)133 499.2 R .65 +(gers in the stored database metadata.)-.15 F .65 +(The number should represent the order)5.65 F .749(as an inte)133 511.2 R .749 +(ger; for e)-.15 F .749(xample, big endian order w)-.15 F .749 +(ould be the number 4,321.)-.1 F(If)5.749 E F2(lor)3.249 E(der)-.37 E F0 .749 +(is 0 \(no order is)3.249 F(speci\214ed\) the current host order is used.)133 +523.2 Q 1.284(If the log already e)108 540 R 1.284(xists, the v)-.15 F 1.285(a\ +lues speci\214ed for the parameters max_\214le_size and lorder are ignored in) +-.25 F -.1(fa)108 552 S -.2(vo)-.1 G 2.5(ro).2 G 2.5(ft)136.1 552 S(he v)144.71 +552 Q(alues used when the log w)-.25 E(as created.)-.1 E F1(DB OPERA)72 568.8 Q +(TIONS)-.855 E F0 .687(The data part of the k)108 580.8 R -.15(ey)-.1 G .686(/\ +data pair used by the log access method is the same as for other access method\ +s.).15 F .837(The k)108 592.8 R 1.137 -.15(ey i)-.1 H 3.337(sd).15 G(if)159.421 +592.8 Q 3.337(ferent. Each)-.25 F .837(log record is identi\214ed by a log seq\ +uence number \(LSN\), which is stored in a)3.337 F(DBT)108 604.8 Q 2.702(,a) +-.74 G .202(nd which is used as the)136.902 604.8 R F2 -.1(ke)2.702 G(y)-.2 E +F0 .202(for all log functions that tak)2.702 F(e)-.1 E F2 -.1(ke)2.701 G(y)-.2 +E F0(ar)2.701 E 2.701(guments. Applications)-.18 F .201(cannot create)2.701 F +(LSN')108 616.8 Q .539(s, and all LSN')-.55 F 3.039(sp)-.55 G(ro)203.216 616.8 +Q .539(vided to functions as ar)-.15 F .539(guments must \214rst be retrie)-.18 +F -.15(ve)-.25 G 3.04(du).15 G .54(sing the)440.37 616.8 R F2(put)3.04 E F0(or) +3.04 E F2(seq)3.04 E F0(func-)3.04 E 2.783(tions. T)108 628.8 R 2.783(op)-.8 G +(ro)153.326 628.8 Q .283(vide a distinguished v)-.15 F .282 +(alue for applications, it is guaranteed that no v)-.25 F .282(alid LSN will e) +-.25 F -.15(ve)-.25 G 2.782(rh).15 G -2.25 -.2(av e)519.248 628.8 T(a)2.982 E +(size of 0.)108 640.8 Q(Applications can compare LSN')108 657.6 Q 2.5(su)-.55 G +(sing the)247.98 657.6 Q F2(lo)2.5 E(g_lsn_compar)-.1 E(e)-.37 E F0 +(function \(see belo)2.5 E(w\).)-.25 E .429(Applications can associate LSN')108 +674.4 R 2.929(sw)-.55 G .429(ith speci\214c log \214les.)253.586 674.4 R .429 +(The function)5.429 F F2(lo)2.929 E(g_lsn_\214le)-.1 E F0 .43(\(see belo)2.93 F +.43(w\), returns the)-.25 F .214 +(name of the log \214le containing the record with a speci\214ed LSN.)108 686.4 +R .214(\(The mapping of LSN to \214le is needed for)5.214 F(4.4 Berk)72 732 Q +(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(3, 1995) +2.5 E(1)535 732 Q EP +%%Page: 2 11 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_LOG\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_LOG\(3\))340.17 48 R .397(database administration.) +108 84 R -.15(Fo)5.397 G 2.897(re).15 G .398 +(xample, a transaction manager typically records the earliest LSN needed for) +231.931 84 R .519(restart, and the database administrator may w)108 96 R .519 +(ant to archi)-.1 F .819 -.15(ve l)-.25 H .519(og \214les to tape when the).15 +F 3.018(yc)-.15 G .518(ontain only LSN')465.624 96 R(s)-.55 E +(before the earliest one needed for restart.\))108 108 Q +(Applications can truncate the log \214le up to a speci\214c LSN using the)108 +124.8 Q/F1 10/Times-Italic@0 SF(lo)2.5 E(g_trunc)-.1 E F0(function \(see belo) +2.5 E(w\).)-.25 E .221(The functions returned by)108 141.6 R F1(db_open)2.721 E +F0 .221(for the log access method are as described in)2.721 F F1(db_open)2.721 +E F0 2.722(,w).24 G .222(ith the follo)482.586 141.6 R(w-)-.25 E(ing e)108 +153.6 Q(xceptions and additions:)-.15 E 5.28(type The)108 170.4 R +(type is DB_LOG.)2.5 E 10.28(del The)108 187.2 R F1(del)3.505 E F0 1.005 +(function al)3.505 F -.1(wa)-.1 G 1.005 +(ys returns an error for the log-manager access method, setting).1 F F1(errno) +3.504 E F0 1.004(to EIN-)3.504 F -1.35(VA)133 199.2 S(L.)1.35 E +(int \(*log_\215ush\)\(const DB *db, const DBT *lsn\);)108 216 Q(The)133 228 Q +F1(lo)2.866 E(g_\215ush)-.1 E F0 .367 +(function \215ushes the log up to and including the log record)2.866 F F1(lsn) +2.867 E F0 5.367(.T).24 G .367(he function)454.926 228 R F1(lo)2.867 E +(g_\215ush)-.1 E F0(returns -1 on f)133 240 Q(ailure, setting)-.1 E F1(errno) +2.5 E F0 2.5(,a).18 G(nd 0 on success.)278.61 240 Q +(int \(*log_lsn_compare\)\(const DB *,)108 256.8 Q .255 +(const DBT *lsn1, const DBT *lsn2\); A pointer to a function which is pro)183 +268.8 R .255(vided to permit)-.15 F .312(applications to compare LSN')133 280.8 +R 2.812(s. The)-.55 F F1(lo)2.812 E(g_lsn_compar)-.1 E(e)-.37 E F0 .312 +(function returns an inte)2.812 F .313(ger less than, equal to,)-.15 F .058 +(or greater than zero if the \214rst LSN is considered to be respecti)133 292.8 +R -.15(ve)-.25 G .058(ly less than, equal to, or greater than).15 F +(the second LSN.)133 304.8 Q(int \(*log_lsn_\214le\)\(const DB *db,)108 321.6 Q +(const DBT *lsn, char *name\);)183 333.6 Q(The)133 345.6 Q F1(lo)3.21 E +(g_lsn_\214le)-.1 E F0 .71 +(function stores a pointer to the name of the \214le containing)3.21 F F1(lsn) +3.211 E F0 .711(in the address refer)3.211 F(-)-.2 E .293(enced by)133 357.6 R +F1(name)2.793 E(.)-.15 E F0 .293(This pointer is to an internal static object,\ + and subsequent calls to the same function)5.293 F +(will modify the same object.)133 369.6 Q(The function)133 386.4 Q F1(lo)2.5 E +(g_lsn_\214le)-.1 E F0(returns -1 on f)2.5 E(ailure, setting)-.1 E F1(errno)2.5 +E F0 2.5(,a).18 G(nd 0 on success.)381.56 386.4 Q +(int \(*log_unlink\)\(const char *path, int force\);)108 403.2 Q(The)133 415.2 +Q F1(lo)3.275 E(g_unlink)-.1 E F0 .775(function destro)3.275 F .775 +(ys the log represented by)-.1 F F1(path)3.275 E F0 5.775(.I).28 G 3.275(ft) +394.745 415.2 S(he)404.13 415.2 Q F1(for)3.275 E(ce)-.37 E F0 .776 +(parameter is not set to 1)3.275 F .725 +(and there are other processes using the log, then)133 427.2 R F1(lo)3.224 E +(g_unlink)-.1 E F0 .724(will return -1, setting)3.224 F F1(errno)3.224 E F0 +.724(to EB)3.224 F(USY)-.1 E(.)-1.29 E(If)133 439.2 Q F1(for)2.831 E .331 +(ce is not set or ther)-.37 F 2.831(ea)-.37 G 1.071 -.37(re n)244.287 439.2 T +2.831(op).37 G -.45(ro)272.909 439.2 S .331(cesses using the lo).45 F .532 -.1 +(g, t)-.1 H .332(hen all \214les).1 F F0 .332(used by the log are destro)2.832 +F(yed.)-.1 E F1(lo)133 451.2 Q(g_unlink)-.1 E F0(will return -1 on f)2.5 E +(ailure, setting)-.1 E F1(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)337.96 +451.2 Q(int \(*log_trunc\)\(const DB *db, const DBT *lsn\);)108 468 Q(The)133 +480 Q F1(lo)2.601 E(g_trunc)-.1 E F0 .101 +(function truncates the log up to an LSN which is less than)2.601 F F1(lsn)2.6 +E F0 5.1(.T).24 G .1(he function)453.24 480 R F1(lo)2.6 E(g_trunc)-.1 E F0 +(returns -1 on f)133 492 Q(ailure, setting)-.1 E F1(errno)2.5 E F0 2.5(,a).18 G +(nd 0 on success.)278.61 492 Q 9.72(put A)108 508.8 R .339 +(log record containing)2.839 F F1(data)2.839 E F0 .339(is appended to the log.) +2.839 F(Unlik)5.339 E 2.84(et)-.1 G(he)382.44 508.8 Q F1(put)2.84 E F0 .34 +(functions for other access meth-)2.84 F .789(ods, the k)133 520.8 R 1.089 -.15 +(ey p)-.1 H .788(arameter is not initialized by the application, instead, the \ +LSN assigned to the data is).15 F(returned in the)133 532.8 Q F1 -.1(ke)2.5 G +(y)-.2 E F0(parameter)2.5 E(.)-.55 E 1.157(The caller is responsible for pro) +133 549.6 R 1.157(viding an)-.15 F 3.657(yn)-.15 G 1.157(ecessary structure to) +318.267 549.6 R F1 1.157(data .)3.657 F F0(\(F)6.157 E 1.157(or e)-.15 F 1.157 +(xample, in a write-)-.15 F .267 +(ahead logging protocol, the application must understand what part of)133 561.6 +R F1(data)2.767 E F0 .266(is an operation code, what)2.766 F .622 +(part is redo information, and what part is undo information.)133 573.6 R .622 +(In addition, most transaction managers)5.622 F .985(will store in)133 585.6 R +F1(data)3.485 E F0 .985(the LSN of the pre)3.485 F .984 +(vious log record for the same transaction, to support chaining)-.25 F +(back through the transaction')133 597.6 Q 2.5(sl)-.55 G +(og records during undo.\))258.54 597.6 Q(The parameter)133 614.4 Q F1<8d61>2.5 +E(g)-.1 E F0(must be set to 0 or e)2.5 E(xactly one of the follo)-.15 E(wing v) +-.25 E(alues:)-.25 E(R_CHECKPOINT)133 631.2 Q .5(Specify the k)158 643.2 R -.15 +(ey)-.1 G .5(/data pair of the current call as the one to be returned when the) +.15 F F1(seq)3 E F0 .5(function is)3 F(ne)158 655.2 Q +(xt called with the R_CHECKPOINT \215ag.)-.15 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5 +(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(3, 1995)2.5 E(2)535 +732 Q EP +%%Page: 3 12 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_LOG\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_LOG\(3\))340.17 48 R(R_FLUSH)133 84 Q +(Flush immediately \(ignoring an)158 96 Q 2.5(yp)-.15 G +(ossibility for group commit\).)296.74 96 Q 9.17(seq The)108 112.8 R/F1 10 +/Times-Italic@0 SF(seq)2.5 E F0(function tak)2.5 E(es the follo)-.1 E +(wing additional \215ag:)-.25 E(R_CHECKPOINT)133 129.6 Q .184(The last k)158 +141.6 R -.15(ey)-.1 G .184(/data pair stored by the).15 F F1(put)2.684 E F0 +.183(function \(using the R_CHECKPOINT \215ag\) is returned,)2.684 F .216 +(and the cursor is set or initialized to reference it.)158 153.6 R .216(The e) +5.216 F .216(xpected use of this \215ag is during restart)-.15 F .801 +(and to determine what part of the log must be a)158 165.6 R -.25(va)-.2 G .801 +(ilable for restart.).25 F .801(Therefore, the log record)5.801 F(retrie)158 +177.6 Q -.15(ve)-.25 G 3.352(dw).15 G .853 +(ith R_CHECKPOINT should contain all the information that the transaction man-) +203.712 177.6 R(ager will need for this purpose.)158 189.6 Q 4.17(sync The)108 +206.4 R F1(sync)3.135 E F0 .635(function al)3.135 F -.1(wa)-.1 G .635 +(ys returns an error for the log-manager access method, setting).1 F F1(errno) +3.134 E F0 .634(to EIN-)3.134 F -1.35(VA)133 218.4 S(L.)1.35 E/F2 9 +/Times-Bold@0 SF(SEE ALSO)72 235.2 Q F1(db_btr)108 247.2 Q(ee)-.37 E F0(\(3\),) +.18 E F1(db_hash)2.5 E F0(\(3\),).28 E F1(db_loc)2.5 E(k)-.2 E F0(\(3\),).67 E +F1(db_mpool)2.5 E F0(\(3\),).51 E F1(db_open)2.5 E F0(\(3\),).24 E F1(db_r)2.5 +E(ecno)-.37 E F0(\(3\),).18 E F1(db_txn)2.5 E F0(\(3\)).24 E(4.4 Berk)72 732 Q +(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(3, 1995) +2.5 E(3)535 732 Q EP +%%Page: 1 13 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 110.12(DB_MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 110.12(anual DB_MPOOL\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA) +72 84 S(ME).18 E F0(db_mpool \255 general purpose shared memory b)108 96 Q(uf) +-.2 E(fer pool)-.25 E F1(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF +(#include )-.4 E(#include )108 136.8 Q(int)108 +160.8 Q(mpool_cr)108 172.8 Q +(eate\(char *path, mode_t mode, size_t cachesize, u_long \215ags\);)-.18 E +(MPOOL *)108 196.8 Q(mpool_open\(char *path\);)108 208.8 Q(int)108 232.8 Q +(mpool_close\(MPOOL *mp\);)108 244.8 Q(MPOOLFILE *)108 268.8 Q(mpool_f)108 +280.8 Q(open\(MPOOL *mp, char *path, size_t pagesize, v)-.25 E(oid *pgcookie,) +-.1 E(int \(*pgin\)\(MPOOLFILE *mpf)158 292.8 Q(,)-.15 E(pgno_t pgno, v)188 +304.8 Q(oid *pgaddr)-.1 E 2.5(,v)-.92 G(oid *pgcookie\),)311.91 304.8 Q +(int \(*pgout\)\(MPOOLFILE *mpf)158 316.8 Q(,)-.15 E(pgno_t pgno, v)188 328.8 Q +(oid *pgaddr)-.1 E 2.5(,v)-.92 G(oid *pgcookie\);)311.91 328.8 Q(int)108 352.8 +Q(mpool_fclose\(MPOOLFILE *mpf\);)108 364.8 Q -.1(vo)108 388.8 S(id *).1 E +(mpool_get\(MPOOLFILE *mpf)108 400.8 Q 2.5(,p)-.15 G(gno_t *pgnoaddr)252.02 +400.8 Q 2.5(,u)-.92 G(_long \215ags,)334.73 400.8 Q +(int \(*callback\)\(MPOOLFILE *mpf)158 412.8 Q 2.5(,p)-.15 G(gno_t pgno\)\);) +318.97 412.8 Q(int)108 436.8 Q(mpool_put\(MPOOLFILE *mpf)108 448.8 Q 2.5(,v) +-.15 G(oid *pgaddr)253.04 448.8 Q 2.5(,u)-.92 G(_long \215ags\);)314.64 448.8 Q +(int)108 472.8 Q(mpool_sync\(MPOOLFILE *mpf\);)108 484.8 Q(int)108 508.8 Q +(mpool_unlink\(const char *path, int f)108 520.8 Q(or)-.25 E(ce\);)-.18 E -.1 +(vo)108 544.8 S(id).1 E(mpool_stat\(MPOOL *mp, FILE *fp\);)108 556.8 Q F1 +(DESCRIPTION)72 573.6 Q F0 .485(The DB library is a f)108 585.6 R .485 +(amily of groups of functions that pro)-.1 F .486 +(vides a modular programming interf)-.15 F .486(ace to trans-)-.1 F .823 +(actions and record-oriented \214le access.)108 597.6 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 609.6 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 609.6 S .258(ell as v)237.082 609.6 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 609.6 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 609.6 S .258 +(he functional groups \(e.g.)436.746 609.6 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 621.6 R .306(tional groups are e)108 633.6 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 633.6 Q .245(description of transactions, see)108 645.6 R/F3 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F3(db_open)2.745 E +F0(\(3\)).24 E .307(and then the indi)108 657.6 R .307 +(vidual access method manual pages:)-.25 F F3(db_btr)2.808 E(ee)-.37 E F0 +(\(3\),).18 E F3(db_hash)2.808 E F0(\(3\),).28 E F3(db_lo)2.808 E(g)-.1 E F0 +.308(\(3\) and).22 F F3(db_r)2.808 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +669.6 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +669.6 R 3.635(,s)-.4 G(ee)307.32 669.6 Q F3(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 681.6 Q 2.5(,s)-.4 G(ee)171.2 681.6 Q F3(db_mpool)2.5 E F0 +(\(3\).).51 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q +99.315(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 14 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 110.12(DB_MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 110.12(anual DB_MPOOL\(3\))340.17 48 R +(This manual page describes speci\214c details of the memory pool interf)108 84 +Q(ace.)-.1 E(The)108 100.8 Q/F1 10/Times-Italic@0 SF(db_mpool)3.682 E F0 1.182 +(function is the library interf)3.682 F 1.183(ace intended to pro)-.1 F 1.183 +(vide general-purpose, page-oriented b)-.15 F(uf)-.2 E(fer)-.25 E .16 +(management of one or more \214les.)108 112.8 R .16(While designed to w)5.16 F +.16(ork with the other DB functions, these functions are)-.1 F .604 +(also useful for more general purposes.)108 124.8 R .604 +(The memory pools \(MPOOL)5.604 F -.55('s)-.92 G 3.104(\)a).55 G .605 +(re referred to in this document as)404.18 124.8 R .985(simply `)108 136.8 R +(`pools')-.74 E 3.485('. Pools)-.74 F .985(may be shared between processes.) +3.485 F .985(Pools are usually \214lled by pages from one or)5.985 F .673 +(more \214les \(MPOOLFILE')108 148.8 R 3.173(s\). P)-.55 F .674 +(ages in the pool are replaced in LR)-.15 F 3.174(U\()-.4 G .674 +(least-recently-used\) order)392.318 148.8 R 3.174(,w)-.4 G .674(ith each) +507.946 148.8 R(ne)108 160.8 Q 4.243(wp)-.25 G 1.743 +(age replacing the page which has been unused the longest.)133.653 160.8 R -.15 +(Pa)6.742 G 1.742(ges retrie).15 F -.15(ve)-.25 G 4.242(df).15 G 1.742 +(rom the pool using)459.494 160.8 R F1(mpool_g)108 172.8 Q(et)-.1 E F0 1.255 +(are `)3.755 F(`pinned')-.74 E 3.755('i)-.74 G 3.755(nm)215.435 172.8 S(emory) +231.97 172.8 Q 3.755(,b)-.65 G 3.755(yd)268.125 172.8 S(ef)281.88 172.8 Q 1.256 +(ault, until the)-.1 F 3.756(ya)-.15 G 1.256(re returned to the pool using the) +358.168 172.8 R F1(mpool_put)3.756 E F0(function.)108 184.8 Q .934 +(The function)108 201.6 R F1(mpool_cr)3.434 E(eate)-.37 E F0 .934 +(creates and initializes the memory pool identi\214ed by the)3.434 F F1(path) +3.433 E F0(directory)3.433 E 5.933(.T)-.65 G(his)528.33 201.6 Q .931 +(directory must already e)108 213.6 R .931(xist when)-.15 F F1(mpool_cr)3.431 E +(eate)-.37 E F0 .931(is called.)3.431 F .932 +(If the memory pool identi\214ed by)5.931 F F1(path)3.432 E F0(already)3.432 E +-.15(ex)108 225.6 S .045(ists, then).15 F F1(mpool_cr)2.545 E(eate)-.37 E F0 +.045(returns success without further action.)2.545 F .045 +(The \214les associated with the memory pool)5.045 F .87 +(are created in the directory speci\214ed by)108 237.6 R F1(path)3.37 E F0 5.87 +(.\().28 G .87(The group of the created \214les is based on the system and) +304.08 237.6 R .258(directory def)108 249.6 R .258 +(aults, and is not further speci\214ed by)-.1 F F1(mpool_cr)2.758 E(eate)-.37 E +F0 2.758(.\) All).18 F .258(\214les created by)2.758 F F1(mpool_cr)2.758 E +(eate)-.37 E F0 .258(are cre-)2.758 F .048(ated with mode)108 261.6 R F1(mode) +2.548 E F0 .049(\(as described in)2.548 F F1 -.15(ch)2.549 G(mod).15 E F0 .049 +(\(2\)\) and modi\214ed by the process' umask v).77 F .049(alue \(see)-.25 F F1 +(umask)2.549 E F0(\(2\)\).).67 E(The)108 278.4 Q F1(cac)2.544 E(hesize)-.15 E +F0(ar)2.544 E .044(gument speci\214es the size of the pool in bytes, and shoul\ +d be the size of the normal w)-.18 F(orking)-.1 E .509(set of the application \ +with some small amount of additional memory for unusual situations.)108 290.4 R +.509(If the number)5.509 F .362(of bytes currently `)108 302.4 R(`pinned')-.74 +E 2.862('i)-.74 G 2.862(nm)226.828 302.4 S .362(emory e)242.47 302.4 R(xceeds) +-.15 E F1(cac)2.861 E(hesize)-.15 E F0 2.861(,t).18 G(he)351.734 302.4 Q F1 +(db_mpool)2.861 E F0 .361(functions will attempt to allocate)2.861 F +(more memory and do not necessarily f)108 314.4 Q(ail, although the)-.1 E 2.5 +(ym)-.15 G(ay suf)341.61 314.4 Q(fer performance de)-.25 E(gradation.)-.15 E +(The)108 331.2 Q F1<8d61>2.5 E(gs)-.1 E F0(ar)2.5 E(gument is set by)-.18 E F1 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)272.73 331.2 S(he follo)281.34 +331.2 Q(wing v)-.25 E(alues:)-.25 E(MPOOL_PRIV)108 348 Q -1.11(AT)-1.35 G(E) +1.11 E(The pool is not shared by other processes or threads, so no locking of \ +pool resources is required.)144 360 Q .115(The function)108 376.8 R F1 +(mpool_open)2.615 E F0 .115 +(returns a pointer to the memory pool identi\214ed by)2.615 F F1(path)2.615 E +F0 2.615(,w).28 G .115(hich must ha)447.525 376.8 R .415 -.15(ve a)-.2 H +(lready).15 E .036(been created by a call to)108 388.8 R F1(mpool_cr)2.536 E +(eate)-.37 E F0 5.036(.T).18 G .036(he process must ha)276.074 388.8 R .336 +-.15(ve p)-.2 H .036(ermission to read and write \214les with o).15 F(wn-)-.25 +E 1.157(ers, groups and permissions as described for)108 400.8 R F1(mpool_cr) +3.657 E(eate)-.37 E F0 6.157(.T).18 G(he)365.075 400.8 Q F1(mpool_open)3.657 E +F0 1.157(function returns NULL on)3.657 F -.1(fa)108 412.8 S(ilure, setting).1 +E F1(errno)2.5 E F0(.).18 E(The)108 429.6 Q F1(mpool_close)6.383 E F0 3.883 +(function closes the pool indicated by the MPOOL pointer)6.383 F F1(mp)6.383 E +F0 6.383(,a).19 G 6.382(sr)480.026 429.6 S 3.882(eturned by)493.628 429.6 R F1 +(mpool_open)108 441.6 Q F0 5.047(.T).24 G .047(his function does)171.337 441.6 +R/F2 10/Times-Bold@0 SF(not)2.547 E F0 .047(imply a call to)2.547 F F1 +(mpool_sync)2.547 E F0 .047(\(or to)2.547 F F1(mpool_fclose)2.547 E F0 2.547 +(\)i).18 G .047(.e. no pages are writ-)455.951 441.6 R .404 +(ten to the source \214le as as a result of calling)108 453.6 R F1(mpool_close) +2.904 E F0 5.404(.T).18 G .404(he function)354.658 453.6 R F1(mpool_close)2.904 +E F0 .403(returns -1 on f)2.904 F(ailure,)-.1 E(setting)108 465.6 Q F1(errno) +2.5 E F0 2.5(,a).18 G(nd 0 on success.)169.01 465.6 Q .827(The function)108 +482.4 R F1(mpool_fopen)3.327 E F0 .827(opens a \214le for b)3.327 F(uf)-.2 E +.828(fering in the pool speci\214ed by the MPOOL ar)-.25 F 3.328(gument. The) +-.18 F F1(path)108 494.4 Q F0(ar)2.85 E .349 +(gument is the name of the \214le to be opened.)-.18 F(The)5.349 E F1(pa)2.849 +E -.1(ge)-.1 G(size).1 E F0(ar)2.849 E .349 +(gument is the size, in bytes, of the unit)-.18 F .738(of transfer between the\ + application and the pool, although not necessarily the unit of transfer betwe\ +en the)108 506.4 R .12(pool and the source \214le.)108 518.4 R .12 +(Applications not kno)5.12 F .12 +(wing the page size of the source \214le should retrie)-.25 F .42 -.15(ve t) +-.25 H .12(he meta-).15 F .234(data from the \214le using a page size that is \ +correct for the metadata, then close and reopen the \214le, or)108 530.4 R +2.735(,o)-.4 G(ther)521.32 530.4 Q(-)-.2 E +(wise determine the page size before calling)108 542.4 Q F1(mpool_fopen)2.5 E +F0(.).24 E .416(If the)108 559.2 R F1(pgin)2.916 E F0 .416(function is speci\ +\214ed, it is called each time a page is read into the memory pool from the so\ +urce)2.916 F 2.835(\214le. If)108 571.2 R(the)2.835 E F1(pgout)2.835 E F0 .336 +(function is speci\214ed, it is called each time a page is written to the sour\ +ce \214le.)2.835 F .336(Both func-)5.336 F .834 +(tions are called with the MPOOLFILE pointer returned from)108 583.2 R F1 +(mpool_fopen)3.333 E F0 3.333(,t).24 G .833(he page number)421.815 583.2 R +3.333(,ap)-.4 G .833(ointer to)505.557 583.2 R .014 +(the page being read or written, and the ar)108 595.2 R(gument)-.18 E F1 +(pgcookie)2.515 E F0 5.015(.I).18 G 2.515(fe)351.695 595.2 S .015 +(ither function f)361.98 595.2 R .015(ails, it should return non-zero)-.1 F +(and set)108 607.2 Q F1(errno)2.5 E F0 2.5(,i).18 G 2.5(nw)168.73 607.2 S +(hich case the)183.45 607.2 Q F1(db_mpool)2.5 E F0 +(function calling it will also f)2.5 E(ail, lea)-.1 E(ving)-.2 E F1(errno)2.5 E +F0(intact.)2.5 E(The)108 624 Q F1(mpool_fclose)2.705 E F0 .204 +(function closes the source \214le indicated by the MPOOLFILE pointer)2.705 F +F1(mpf)2.704 E F0 5.204(.T)1.96 G .204(his function)492.296 624 R(does)108 636 +Q F2(not)3.615 E F0 1.115(imply a call to)3.615 F F1(mpool_sync)3.615 E F0 +3.615(,i).31 G 1.115 +(.e. no pages are written to the source \214le as as a result of calling) +268.885 636 R F1(mpool_fclose)108 648 Q F0 5(.T).18 G(he function)175.12 648 Q +F1(mpool_fclose)2.5 E F0(returns -1 on f)2.5 E(ailure, setting)-.1 E F1(errno) +2.5 E F0 2.5(,a).18 G(nd 0 on success.)424.33 648 Q .019(The function)108 664.8 +R F1(mpool_g)2.519 E(et)-.1 E F0 .019 +(returns a pointer to the page with the page number speci\214ed by)2.519 F F1 +(pgnoaddr)2.518 E F0 2.518(,f).73 G .018(rom the)509.152 664.8 R .986 +(source \214le speci\214ed by the MPOOLFILE pointer)108 676.8 R F1(mpf)3.486 E +F0 5.986(.I)1.96 G 3.486(ft)342.268 676.8 S .987(he page does not e)351.864 +676.8 R .987(xist or cannot be retrie)-.15 F -.15(ve)-.25 G(d,).15 E F1 +(mpool_g)108 688.8 Q(et)-.1 E F0(returns NULL and sets errno.)2.5 E(4.4 Berk)72 +732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F +(1, 1995)2.5 E(2)535 732 Q EP +%%Page: 3 15 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 110.12(DB_MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 110.12(anual DB_MPOOL\(3\))340.17 48 R(The)108 84 Q/F1 10 +/Times-Italic@0 SF<8d61>2.5 E(gs)-.1 E F0(ar)2.5 E(gument is set by)-.18 E F1 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)272.73 84 S(he follo)281.34 84 +Q(wing v)-.25 E(alues:)-.25 E(MPOOL_CALLB)108 100.8 Q -.4(AC)-.35 G(K).4 E 1.04 +(After the page number has been determined, b)133 112.8 R 1.04(ut before an)-.2 +F 3.54(yo)-.15 G 1.04(ther process or thread can access the)388.26 112.8 R .471 +(page, the function speci\214ed by the)133 124.8 R F1(callbac)2.971 E(k)-.2 E +F0(ar)2.971 E .471(gument is called.)-.18 F .471(If the function f)5.471 F .472 +(ails, it should return)-.1 F 1.11(non-zero and set)133 136.8 R F1(errno)3.61 E +F0 3.61(,i).18 G 3.61(nw)236.21 136.8 S 1.11(hich case)252.04 136.8 R F1 +(mpool_g)3.61 E(et)-.1 E F0 1.11(will also f)3.61 F 1.11(ail, lea)-.1 F(ving) +-.2 E F1(errno)3.61 E F0 3.61(intact. The)3.61 F F1(callbac)3.61 E(k)-.2 E F0 +1.012(function is called with the MPOOLFILE pointer returned from)133 148.8 R +F1(mpool_fopen)3.512 E F0 1.013(and the page number)3.513 F(.)-.55 E .228 +(This functionality is commonly used when page locking is required, b)133 160.8 +R .227(ut the page number of the page)-.2 F(being retrie)133 172.8 Q -.15(ve) +-.25 G 2.5(di).15 G 2.5(sn)198.14 172.8 S(ot kno)209.53 172.8 Q(wn.)-.25 E +(MPOOL_CREA)108 189.6 Q(TE)-1.11 E(If the speci\214ed page does not e)133 201.6 +Q(xist, create it.)-.15 E(MPOOL_LAST)108 218.4 Q 2.105 +(Return the last page of the source \214le and cop)133 230.4 R 4.605(yi)-.1 G +2.106(ts page number to the location referenced by)347.25 230.4 R F1(pgnoaddr) +133 242.4 Q F0(.).73 E(MPOOL_NEW)108 259.2 Q(Create a ne)133 271.2 Q 2.5(wp) +-.25 G(age in the \214le and cop)192.45 271.2 Q 2.5(yi)-.1 G +(ts page number to the location referenced by)290.67 271.2 Q F1(pgnoaddr)2.5 E +F0(.).73 E(MPOOL_NOPIN)108 288 Q(Don')133 300 Q 2.918(tp)-.18 G .418 +(in the page into memory)164.068 300 R 5.418(.\()-.65 G .417 +(This \215ag is intended for deb)274.108 300 R .417(ugging purposes, when it') +-.2 F 2.917(so)-.55 G .417(ften use-)504.873 300 R .972(ful to e)133 312 R .972 +(xamine pages which are currently held by other parts of the application.)-.15 +F -.15(Pa)5.973 G .973(ges retrie).15 F -.15(ve)-.25 G 3.473(di).15 G(n)535 312 +Q .529(this manner don')133 324 R 3.029(tn)-.18 G .528 +(eed to be returned to the memory pool, i.e. the)212.457 324 R 3.028(ys)-.15 G +(hould)413.95 324 Q/F2 10/Times-Bold@0 SF(not)3.028 E F0 .528 +(be speci\214ed as ar)3.028 F(gu-)-.18 E(ments to the)133 336 Q F1(mpool_put) +2.5 E F0(routine.\))2.5 E(Created pages ha)108 352.8 Q .3 -.15(ve a)-.2 H +(ll their bytes set to 0.).15 E 2.078(All pages returned by)108 369.6 R F1 +(mpool_g)4.578 E(et)-.1 E F0 2.079 +(\(unless the MPOOL_NOPIN \215ag is speci\214ed\), will be retained \(i.e.) +4.578 F -.74(``)108 381.6 S(pinned').74 E +('\) in the pool until a subsequent call to)-.74 E F1(mpool_put)2.5 E F0(.).68 +E .077(The function)108 398.4 R F1(mpool_put)2.577 E F0 .076 +(indicates that the page referenced by)2.577 F F1(pgaddr)2.576 E F0 .076 +(can be e)2.576 F .076(victed from the pool.)-.25 F F1(Pgaddr)5.076 E F0 +(must be an address pre)108 410.4 Q(viously returned by)-.25 E F1(mpool_g)2.5 E +(et)-.1 E F0(.).68 E(The \215ag v)108 427.2 Q(alue is speci\214ed by)-.25 E F1 +(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)277.2 427.2 S(he follo)285.81 +427.2 Q(wing v)-.25 E(alues:)-.25 E(MPOOL_DIR)108 444 Q(TY)-.6 E .052(The page\ + has been modi\214ed and must be written to the source \214le before being e) +133 456 R .052(victed from the pool.)-.25 F(MPOOL_DISCARD)108 472.8 Q .145 +(The page is unlik)133 484.8 R .144(ely to be useful in the near future, and s\ +hould be discarded before other pages in the)-.1 F(pool.)133 496.8 Q +(The function)108 513.6 Q F1(mpool_put)2.5 E F0(returns -1 on f)2.5 E +(ailure, setting)-.1 E F1(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)352.77 +513.6 Q .027(The function)108 530.4 R F1(mpool_sync)2.527 E F0 .028 +(writes all pages associated with the MPOOLFILE pointer)2.528 F F1(mpf)2.528 E +F0 2.528(,w)1.96 G .028(hich were speci-)474.414 530.4 R .431(\214ed as ar)108 +542.4 R .431(guments to the)-.18 F F1(mpool_put)2.931 E F0 .431 +(function with an associated \215ag of MPOOL_DIR)2.931 F(TY)-.6 E 2.93(,t)-1.29 +G 2.93(ot)472.61 542.4 S .43(he source \214le.)483.32 542.4 R(The function)108 +554.4 Q F1(mpool_sync)2.5 E F0(returns -1 on f)2.5 E(ailure, setting)-.1 E F1 +(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)357.76 554.4 Q 1.075 +(The function)108 571.2 R F1(mpool_unlink)3.575 E F0(destro)3.575 E 1.075 +(ys the memory pool identi\214ed by the directory)-.1 F F1(path)3.575 E F0 +3.575(,r).28 G(emo)471.33 571.2 Q 1.075(ving all \214les)-.15 F 1.121 +(used to implement the memory pool.)108 583.2 R 1.121(\(The directory)6.121 F +F1(path)3.621 E F0 1.121(is not remo)3.621 F -.15(ve)-.15 G 3.62(d.\) If).15 F +1.12(there are processes which)3.62 F(ha)108 595.2 Q .871 -.15(ve c)-.2 H +(alled).15 E F1(mpool_open)3.071 E F0 .571(without calling)3.071 F F1 +(mpool_close)3.071 E F0 .572 +(\(i.e., there are processes currently using the memory)3.071 F(pool\),)108 +607.2 Q F1(mpool_unlink)2.652 E F0 .152(will f)2.652 F .151 +(ail without further action, unless the force \215ag is set, in which case)-.1 +F F1(mpool_unlink)2.651 E F0 .524 +(will attempt to delete the memory pool \214les re)108 619.2 R -.05(ga)-.15 G +.525(rdless of an).05 F 3.025(yp)-.15 G .525 +(rocesses still using the memory pool.)366.45 619.2 R(An)5.525 E(y)-.15 E .598 +(accesses to a remo)108 631.2 R -.15(ve)-.15 G 3.097(dm).15 G .597 +(emory pool will lik)208.95 631.2 R .597(ely result in une)-.1 F .597 +(xpected beha)-.15 F(vior)-.2 E 5.597(.T)-.55 G .597(he function)436.036 631.2 +R F1(mpool_unlink)3.097 E F0(returns -1 on f)108 643.2 Q(ailure, setting)-.1 E +F1(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)253.61 643.2 Q .11 +(In the case of catastrophic or system f)108 660 R .11 +(ailure, it is possible to clean up a memory pool by remo)-.1 F .11 +(ving all of the)-.15 F .569(\214les in the directory speci\214ed to the)108 +672 R F1(mpool_cr)3.068 E(eate)-.37 E F0 .568 +(function, as memory pool \214les are ne)3.068 F -.15(ve)-.25 G 3.068(rc).15 G +.568(reated in an)487.364 672 R(y)-.15 E +(directory other than the one speci\214ed to)108 684 Q F1(mpool_cr)2.5 E(eate) +-.37 E F0(.).18 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q +99.315(ution August)-.2 F(1, 1995)2.5 E(3)535 732 Q EP +%%Page: 4 16 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 110.12(DB_MPOOL\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 110.12(anual DB_MPOOL\(3\))340.17 48 R .025(The function)108 84 R/F1 +10/Times-Italic@0 SF(mpool_stat)2.525 E F0 .026 +(writes statistics for the memory pool)2.526 F F1(mp)2.526 E F0 .026 +(to the \214le speci\214ed by)2.526 F F1(fp)2.526 E F0 5.026(.T).19 G .026 +(hese statistics)485.254 84 R .829 +(include the number of \214les participating in the pool, the acti)108 96 R +1.129 -.15(ve p)-.25 H .829(ages in the pool, and numbers as to ho).15 F(w)-.25 +E(ef)108 108 Q(fecti)-.25 E .3 -.15(ve t)-.25 H(he cache has been.).15 E/F2 9 +/Times-Bold@0 SF(ERR)72 124.8 Q(ORS)-.27 E F0(The)108 136.8 Q F1(mpool_cr)3.852 +E(eate)-.37 E F0(,).18 E F1(mpool_open)3.852 E F0(and)3.852 E F1(mpool_fopen) +3.852 E F0 1.353(functions may f)3.852 F 1.353(ail and set)-.1 F F1(errno)3.853 +E F0 1.353(for an)3.853 F 3.853(yo)-.15 G 3.853(ft)493.424 136.8 S 1.353 +(he errors)503.387 136.8 R(speci\214ed for the library functions)108 148.8 Q F1 +(open)2.5 E F0(\(2\),).24 E F1 -.37(re)2.5 G(ad).37 E F0(\(2\), and).77 E F1 +(malloc)2.5 E F0(\(3\).).31 E(The)108 165.6 Q F1(mpool_close)3.144 E F0(and) +3.144 E F1(mpool_fclose)3.144 E F0 .644(functions may f)3.144 F .644 +(ail and set)-.1 F F1(errno)3.144 E F0 .643(for an)3.143 F 3.143(yo)-.15 G +3.143(ft)425.985 165.6 S .643(he errors speci\214ed for the)435.238 165.6 R +(library functions)108 177.6 Q F1(close)2.5 E F0(\(2\) and).18 E F1(fr)2.5 E +(ee)-.37 E F0(\(3\).).18 E(The)108 194.4 Q F1(mpool_g)4.097 E(et)-.1 E F0 1.597 +(function may f)4.097 F 1.597(ail and set)-.1 F F1(errno)4.097 E F0 1.597 +(for an)4.097 F 4.097(yo)-.15 G 4.097(ft)349.14 194.4 S 1.597 +(he errors speci\214ed for the library functions)359.347 194.4 R F1 -.37(re)108 +206.4 S(ad).37 E F0(\(2\),).77 E F1(write)2.5 E F0(\(2\), and).18 E F1(malloc) +2.5 E F0(\(3\) or the follo).31 E(wing:)-.25 E([EINV)108 223.2 Q(AL])-1.35 E +(The requested page does not e)133 235.2 Q(xist and MPOOL_CREA)-.15 E(TE w) +-1.11 E(as not set.)-.1 E(The)108 252 Q F1(mpool_put)4.288 E F0 1.787 +(function may f)4.287 F 1.787(ail and set)-.1 F F1(errno)4.287 E F0 1.787 +(for an)4.287 F 4.287(yo)-.15 G 4.287(ft)351.701 252 S 1.787 +(he errors speci\214ed for the library function)362.098 252 R F1(write)108 264 +Q F0(\(2\) or the follo).18 E(wing:)-.25 E([EA)108 280.8 Q(CCES])-.4 E +(The source \214le w)133 292.8 Q(as not opened for writing.)-.1 E(The)108 309.6 +Q F1(mpool_sync)3.993 E F0 1.493(function may f)3.993 F 1.493(ail and set)-.1 F +F1(errno)3.993 E F0 1.494(for an)3.993 F 3.994(yo)-.15 G 3.994(ft)353.752 309.6 +S 1.494(he errors speci\214ed for the library function)363.856 309.6 R F1 +(write)108 321.6 Q F0(\(2\).).18 E(The)108 338.4 Q F1(mpool_unlink)3.569 E F0 +1.069(function may f)3.569 F 1.068(ail and set)-.1 F F1(errno)3.568 E F0 1.068 +(for an)3.568 F 3.568(yo)-.15 G 3.568(ft)356.734 338.4 S 1.068 +(he errors speci\214ed for the library function)366.412 338.4 R F1(unlink)108 +350.4 Q F0(\(2\) or the follo).67 E(wing:)-.25 E([EB)108 367.2 Q(USY])-.1 E +(The memory pool w)133 379.2 Q(as in use and the force \215ag w)-.1 E +(as not set.)-.1 E F2(SEE ALSO)72 396 Q F1(db_btr)108 408 Q(ee)-.37 E F0 +(\(3\),).18 E F1(db_hash)2.5 E F0(\(3\),).28 E F1(db_loc)2.5 E(k)-.2 E F0 +(\(3\),).67 E F1(db_lo)2.5 E(g)-.1 E F0(\(3\),).22 E F1(db_open)2.5 E F0 +(\(3\),).24 E F1(db_r)2.5 E(ecno)-.37 E F0(\(3\),).18 E F1(db_txn)2.5 E F0 +(\(3\)).24 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q +99.315(ution August)-.2 F(1, 1995)2.5 E(4)535 732 Q EP +%%Page: 1 17 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 119.01(DB_OPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 119.01(anual DB_OPEN\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA) +72 84 S(ME).18 E F0(db_open \255 database access methods)108 96 Q F1(SYNOPSIS) +72 112.8 Q/F2 10/Times-Bold@0 SF(#include )-.4 E(DB *)108 +148.8 Q(db_open\(const char *\214le, int \215ags, int mode,)108 160.8 Q +(DBTYPE type, DBINFO *dbinf)158 172.8 Q(o, const v)-.25 E(oid *openinf)-.1 E +(o\);)-.25 E F1(DESCRIPTION)72 189.6 Q F0 .485(The DB library is a f)108 201.6 +R .485(amily of groups of functions that pro)-.1 F .486 +(vides a modular programming interf)-.15 F .486(ace to trans-)-.1 F .823 +(actions and record-oriented \214le access.)108 213.6 R .822 +(The library includes support for transaction, locking, logging and)5.822 F +.258(\214le b)108 225.6 R(uf)-.2 E .258(fering functionality)-.25 F 2.758(,a) +-.65 G 2.758(sw)223.214 225.6 S .258(ell as v)237.082 225.6 R .258(arious inde) +-.25 F -.15(xe)-.15 G 2.758(da).15 G .258(ccess methods.)331.434 225.6 R(Man) +5.258 E 2.758(yo)-.15 G 2.758(ft)427.878 225.6 S .258 +(he functional groups \(e.g.)436.746 225.6 R .528(the memory pool functions\) \ +are useful independently of the rest of the DB functions, although some func-) +108 237.6 R .306(tional groups are e)108 249.6 R .306 +(xplicitly based on other functional groups \(e.g.)-.15 F .306 +(transactions and logging\).)5.306 F -.15(Fo)5.306 G 2.806(rag).15 G(eneral) +515.57 249.6 Q .245(description of transactions, see)108 261.6 R/F3 10 +/Times-Italic@0 SF(db_txn)2.745 E F0 2.745(\(3\). F).24 F .245 +(or a general description of the access methods, see)-.15 F F3(db_open)2.745 E +F0(\(3\)).24 E .307(and then the indi)108 273.6 R .307 +(vidual access method manual pages:)-.25 F F3(db_btr)2.808 E(ee)-.37 E F0 +(\(3\),).18 E F3(db_hash)2.808 E F0(\(3\),).28 E F3(db_lo)2.808 E(g)-.1 E F0 +.308(\(3\) and).22 F F3(db_r)2.808 E(ecno)-.37 E F0(\(3\).).18 E -.15(Fo)108 +285.6 S 3.635(rag).15 G 1.135(eneral description of the lock manager)138.45 +285.6 R 3.635(,s)-.4 G(ee)307.32 285.6 Q F3(db_loc)3.635 E(k)-.2 E F0 3.635 +(\(3\). F).67 F 1.135(or a general description of the memory)-.15 F +(pool manager)108 297.6 Q 2.5(,s)-.4 G(ee)171.2 297.6 Q F3(db_mpool)2.5 E F0 +(\(3\).).51 E(This manual page describes the o)108 314.4 Q -.15(ve)-.15 G +(rall structure of the a).15 E -.25(va)-.2 G(ilable access methods.).25 E .457 +(The currently supported \214le formats are btree, hashed, log and recno \(i.e\ +. \215at-\214le oriented\).)108 331.2 R .457(The btree for)5.457 F(-)-.2 E .974 +(mat is a representation of a sorted, balanced tree structure.)108 343.2 R .973 +(The hashed format is an e)5.974 F .973(xtensible, dynamic)-.15 F .801 +(hashing scheme.)108 355.2 R .802 +(The log format is a general-purpose logging f)5.801 F(acility)-.1 E 5.802(.T) +-.65 G .802(he recno format is a byte stream)406.888 355.2 R .415 +(\214le with \214x)108 367.2 R .415(ed or v)-.15 F .415 +(ariable length records.)-.25 F .415(The formats and other)5.415 F 2.914(,f)-.4 +G .414(ormat speci\214c information are described)376.714 367.2 R +(in detail in their respecti)108 379.2 Q .3 -.15(ve m)-.25 H(anual pages:).15 E +F3(db_btr)2.5 E(ee)-.37 E F0(\(3\),).18 E F3(db_hash)2.5 E F0(\(3\),).28 E F3 +(db_lo)2.5 E(g)-.1 E F0(\(3\), and).22 E F3(db_r)2.5 E(ecno)-.37 E F0(\(3\).) +.18 E .138(Db_open opens)108 396 R F3(\214le)2.638 E F0 .139 +(for reading and/or writing.)2.638 F .139(Files ne)5.139 F -.15(ve)-.25 G 2.639 +(ri).15 G .139(ntended to be preserv)349.088 396 R .139 +(ed on disk may be created)-.15 F .423 +(by setting the \214le parameter to NULL.)108 408 R .423 +(\(Note, while most of the access methods use)5.423 F F3(\214le)2.923 E F0 .423 +(as the name of an)2.923 F .429 +(underlying \214le on disk, this is not guaranteed.)108 420 R .43 +(See the manual pages for the indi)5.429 F .43(vidual access methods for)-.25 F +(more information.\))108 432 Q(The)108 448.8 Q F3<8d61>4.328 E(gs)-.1 E F0(and) +4.328 E F3 1.828(mode ar)4.328 F(guments)-.37 E F0 1.828 +(are as speci\214ed to the)4.328 F F3(open)4.328 E F0 1.828(\(2\) function, ho) +.24 F(we)-.25 E -.15(ve)-.25 G 2.628 -.4(r, o).15 H 1.828(nly the O_CREA).4 F +-.74(T,)-1.11 G .127(O_EXCL, O_EXLOCK, O_NONBLOCK, O_RDONL)108 460.8 R 2.708 +-1.29(Y, O)-1 H(_RD)1.29 E .128(WR, O_SHLOCK and O_TR)-.3 F .128 +(UNC \215ags are)-.4 F 2.5(meaningful. \(Note,)108 472.8 R +(opening a database \214le O_WR)2.5 E(ONL)-.4 E 2.5(Yi)-1 G 2.5(sn)342.67 472.8 +S(ot possible.\))354.06 472.8 Q(The)108 489.6 Q F3(type)5.338 E F0(ar)5.338 E +2.837(gument is of type DBTYPE \(as de\214ned in the include \214le\) and may be set to)-.4 F +(DB_BTREE, DB_HASH, DB_LOG or DB_RECNO.)108 501.6 Q(The)108 518.4 Q F3(dbinfo) +3.279 E F0(ar)3.279 E .779(gument is a pointer to a structure containing refer\ +ences to locking, logging, transaction, and)-.18 F 1.242(shared-memory b)108 +530.4 R(uf)-.2 E 1.242(fer pool information.)-.25 F(If)6.242 E F3(dbinfo)3.742 +E F0 1.241(is NULL, then the access method may still use these)3.741 F .667 +(subsystems, b)108 542.4 R .667(ut the usage will be pri)-.2 F -.25(va)-.25 G +.668(te to the application and managed by DB.).25 F(If)5.668 E F3(dbinfo)3.168 +E F0 .668(is non-NULL,)3.168 F .481(then the module referenced by each of the \ +non-NULL \214elds is used by DB as necessary)108 554.4 R 5.48(.T)-.65 G .48 +(he \214elds of the)479.4 554.4 R(DBINFO structure are de\214ned as follo)108 +566.4 Q(ws:)-.25 E(const char *errpfx;)108 583.2 Q 2.5(Ap)133 595.2 S +(re\214x to prepend to error messages; used only if)147.72 595.2 Q F3 +(err\214le)2.5 E F0(is non-NULL.)2.5 E(FILE *err\214le;)108 612 Q(The)133 624 Q +F3(stdio)2.5 E F0(\(3\) \214le stream to which error messages are logged.).18 E +.147(When an)133 648 R 2.647(ye)-.15 G .147(rror occurs in the)180.904 648 R F3 +(db_open)2.648 E F0 .148(function, or in an)2.648 F 2.648(yf)-.15 G .148 +(unction called using a \214eld of the returned)369.824 648 R .234 +(DB structure, an error v)133 660 R .234 +(alue is returned by the function, and the global v)-.25 F(ariable)-.25 E F3 +(errno)2.733 E F0 .233(is set appropri-)2.733 F(ately)133 672 Q 5.415(.I)-.65 G +2.915(ns)163.035 672 S .416(ome cases, ho)174.84 672 R(we)-.25 E -.15(ve)-.25 G +1.216 -.4(r, t).15 H(he).4 E F3(errno)2.916 E F0 -.25(va)2.916 G .416 +(lue may be insuf).25 F .416(\214cient to describe the cause of the error)-.25 +F(.)-.55 E .137(In these cases, if)133 684 R F3(err\214le)2.637 E F0 .137(is n\ +on-NULL, additional error information will be written to the \214le stream it) +2.637 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 18 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 119.01(DB_OPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 119.01(anual DB_OPEN\(3\))340.17 48 R .643 +(represents, preceded by the string, if an)133 84 R 1.943 -.65(y, s)-.15 H .643 +(peci\214ed by).65 F/F1 10/Times-Italic@0 SF(errpfx)3.143 E F0 5.643(.T).53 G +.644(his error logging f)394.94 84 R .644(acility should not)-.1 F +(be required for normal operation, b)133 96 Q(ut may be useful in deb)-.2 E +(ugging applications.)-.2 E(char *errb)108 112.8 Q(uf;)-.2 E .03(The b)133 +124.8 R(uf)-.2 E .03(fer to which error messages are copied.)-.25 F .029 +(If non-NULL,)5.029 F F1(errb)2.529 E(uf)-.2 E F0(beha)2.529 E -.15(ve)-.2 G +2.529(sa).15 G 2.529(sd)451.423 124.8 S .029(escribed for)462.842 124.8 R F1 +(err\214le)2.529 E F0(,).18 E -.15(ex)133 136.8 S .173(cept that the).15 F F1 +(errpfx)2.673 E F0 .174 +(\214eld is ignored and the error message is copied into the speci\214ed b) +2.673 F(uf)-.2 E .174(fer instead)-.25 F 1.014 +(of being written to the FILE stream.)133 148.8 R 1.013 +(The DB routines assume that the associated b)6.014 F(uf)-.2 E 1.013 +(fer is at least)-.25 F(1024 bytes in length.)133 160.8 Q(LOCK_T)108 177.6 Q +(ABLE_T *lockinfo;)-.93 E .265 +(If locking is required for the \214le being opened \(as in the case of b)133 +189.6 R(uf)-.2 E .266(fers being maintained in a shared)-.25 F 1.794(memory b) +133 201.6 R(uf)-.2 E 1.794(fer pool\), the)-.25 F F1(loc)4.294 E(kinfo)-.2 E F0 +1.794(\214eld contains a return v)4.294 F 1.793(alue from the function)-.25 F +F1(loc)4.293 E(k_open)-.2 E F0(that)4.293 E(should be used \(see)133 213.6 Q F1 +(db_loc)2.5 E(k)-.2 E F0 2.5(\(3\)\). If).67 F F1(loc)2.5 E(kinfo)-.2 E F0 +(is NULL, no locking is done.)2.5 E(DB *loginfo;)108 230.4 Q .93 +(If modi\214cations to the \214le being opened should be logged, the)133 242.4 +R F1(lo)3.43 E(ginfo)-.1 E F0 .93(\214eld contains a return v)3.43 F(alue)-.25 +E .063(from the function)133 254.4 R F1(dbopen)2.563 E F0 2.563(,w).24 G .062 +(hen opening a DB \214le of type DB_LOG.)247.642 254.4 R(If)5.062 E F1(lo)2.562 +E(ginfo)-.1 E F0 .062(is NULL, no logging)2.562 F(is done.)133 266.4 Q +(MPOOL *mpoolinfo;)108 283.2 Q 1.129 +(If the cache for the \214le being opened should be maintained in a shared b) +133 295.2 R(uf)-.2 E 1.129(fer pool, the)-.25 F F1(mpoolinfo)3.629 E F0 .102 +(\214eld contains a return v)133 307.2 R .102(alue from the function)-.25 F F1 +(mpool_open)2.602 E F0 .102(that should be used \(see)2.602 F F1(db_mpool)2.602 +E F0 2.602(\(3\)\). If).51 F F1(mpoolinfo)133 319.2 Q F0 .429 +(is NULL, a memory pool may still be created, b)2.929 F .43(ut it will be pri) +-.2 F -.25(va)-.25 G .43(te to the application and).25 F(managed by DB.)133 +331.2 Q(TXNMGR *txninfo;)108 348 Q 1.161 +(If the accesses to the \214le being opened should tak)133 360 R 3.661(ep)-.1 G +1.161(lace in the conte)354.474 360 R 1.161(xt of transactions \(pro)-.15 F +(viding)-.15 E 1.239(atomicity and complete error reco)133 372 R -.15(ve)-.15 G +1.239(ry\), the).15 F F1(txninfo)3.739 E F0 1.239(\214eld contains a return v) +3.739 F 1.24(alue from the function)-.25 F F1(txn_open)133 384 Q F0(\(see)2.599 +E F1(db_txn)2.599 E F0 2.599(\(3\)\). If).24 F .098 +(transactions are speci\214ed, the application is responsible for making suit-) +2.599 F 1.27(able calls to)133 396 R F1(txn_be)3.77 E(gin)-.4 E F0(,).24 E F1 +(txn_abort)3.77 E F0 3.77(,a).68 G(nd)282.91 396 Q F1(txn_commit)3.77 E F0 6.27 +(.I).68 G(f)356.12 396 Q F1(txninfo)3.77 E F0 1.27 +(is NULL, no transaction support is)3.77 F(done.)133 408 Q(The)108 424.8 Q F1 +(openinfo)2.85 E F0(ar)2.85 E .349(gument is a pointer to an access method spe\ +ci\214c structure described in the access method')-.18 F(s)-.55 E .03 +(manual page.)108 436.8 R(If)5.03 E F1(openinfo)2.53 E F0 .031 +(is NULL, each access method will use def)2.53 F .031 +(aults appropriate for the system and the)-.1 F(access method.)108 448.8 Q/F2 9 +/Times-Bold@0 SF(KEY/D)72 465.6 Q -1.35 -.855(AT A)-.315 H -.666(PA)3.105 G +(IRS).666 E F0 .313(Access to all access methods is based on k)108 477.6 R -.15 +(ey)-.1 G .312(/data pairs.).15 F .312(Both k)5.312 F -.15(ey)-.1 G 2.812(sa) +.15 G .312(nd data are represented by the follo)386.758 477.6 R(w-)-.25 E +(ing data structure:)108 489.6 Q(typedef struct {)108 506.4 Q -.2(vo)144 518.4 +S(id *data;).2 E(size_t size;)144 530.4 Q 2.5(}D)108 542.4 S(BT)122.52 542.4 Q +(;)-.55 E(The elements of the DBT structure are de\214ned as follo)108 559.2 Q +(ws:)-.25 E 5.84(data A)108 576 R(pointer to a byte string.)2.5 E 6.95 +(size The)108 592.8 R(length of)2.5 E F1(data)2.5 E F0 2.5(,i).26 G 2.5(nb) +215.2 592.8 S(ytes.)227.7 592.8 Q -2.15 -.25(Ke y)108 609.6 T .672(and data by\ +te strings may reference strings of essentially unlimited length, although an) +3.422 F 3.173(yt)-.15 G .873 -.1(wo o)493.204 609.6 T 3.173(ft).1 G(hem)522.78 +609.6 Q(must \214t into a)108 621.6 Q -.25(va)-.2 G +(ilable memory at the same time.).25 E .14(The access methods pro)108 638.4 R +.139(vide no guarantees about byte string alignment, and applications are resp\ +onsible for)-.15 F(maintaining an)108 650.4 Q 2.5(yn)-.15 G +(ecessary alignment.)180.07 650.4 Q F2(DB OPERA)72 667.2 Q(TIONS)-.855 E F1 +(Db_open)108 679.2 Q F0 .56 +(returns a pointer to a DB structure \(as de\214ned in the include \214le\) on success, and NULL)-.4 F 1.02(on error)108 691.2 R 6.02 +(.T)-.55 G 1.02(he DB structure describes a database type, and includes a set \ +of functions to perform v)155.03 691.2 R(arious)-.25 E(4.4 Berk)72 732 Q(ele) +-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E +(2)535 732 Q EP +%%Page: 3 19 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 119.01(DB_OPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 119.01(anual DB_OPEN\(3\))340.17 48 R .241 +(actions, as described belo)108 84 R 4.041 -.65(w. E)-.25 H .241 +(ach of these functions tak).65 F .241 +(es a pointer to a DB structure, and may tak)-.1 F 2.741(eo)-.1 G .242(ne or) +519.488 84 R .889(more DBT *')108 96 R 3.389(sa)-.55 G .889(nd a \215ag v) +174.827 96 R .889(alue as well.)-.25 F(Indi)5.889 E .888 +(vidual access methods specify additional functions and \215ags)-.25 F +(which are speci\214c to the method.)108 108 Q +(The \214elds of the DB structure are as follo)5 E(ws:)-.25 E(DBTYPE type;)108 +124.8 Q(The type of the underlying access method \(and \214le format\).)133 +136.8 Q(int \(*close\)\(const DB *db\);)108 153.6 Q 3.863(Ap)133 165.6 S 1.363 +(ointer to a function to \215ush an)149.083 165.6 R 3.864(yc)-.15 G 1.364 +(ached information to disk, free an)290.968 165.6 R 3.864(ya)-.15 G 1.364 +(llocated resources, and)445.912 165.6 R .878(close an)133 177.6 R 3.378(yu) +-.15 G .878(nderlying \214les.)179.596 177.6 R .878(Since k)5.878 F -.15(ey)-.1 +G .878(/data pairs are cached in memory).15 F 3.377(,f)-.65 G .877 +(ailing to sync the \214le with)431.445 177.6 R(the)133 189.6 Q/F1 10 +/Times-Italic@0 SF(close)2.5 E F0(or)2.5 E F1(sync)2.5 E F0 +(function may result in inconsistent or lost information.)2.5 E(The)133 206.4 Q +F1(close)2.5 E F0(functions return -1 on f)2.5 E(ailure, setting)-.1 E F1 +(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)355.54 206.4 Q +(int \(*del\)\(const DB *db, TXN *txnid,)108 223.2 Q(const DBT *k)183 235.2 Q +-.15(ey)-.1 G 2.5(,u)-.5 G(_int \215ags\);)257.65 235.2 Q 2.541(Ap)133 247.2 S +.041(ointer to a function to remo)147.761 247.2 R .341 -.15(ve k)-.15 H -.15 +(ey).05 G .041(/data pairs from the database.).15 F .042(The k)5.041 F -.15(ey) +-.1 G .042(/data pair associated with).15 F(the speci\214ed)133 259.2 Q F1 -.1 +(ke)2.5 G(y)-.2 E F0(are discarded from the database.)2.5 E(The)133 276 Q F1 +(txnid)3.317 E F0 .817(parameter contains a transaction ID returned from)3.317 +F F1(txn_be)3.317 E(gin)-.4 E F0 3.317(,i).24 G 3.316(ft)431.22 276 S .816 +(he \214le is being accessed)440.646 276 R +(under transaction protection, or NULL if transactions are not in ef)133 288 Q +(fect.)-.25 E(The parameter)133 304.8 Q F1<8d61>2.5 E(g)-.1 E F0 +(must be set to 0 or e)2.5 E(xactly one of the follo)-.15 E(wing v)-.25 E +(alues:)-.25 E(R_CURSOR)133 321.6 Q(Delete the record referenced by the cursor) +158 333.6 Q 5(.T)-.55 G(he cursor must ha)339.32 333.6 Q .3 -.15(ve p)-.2 H(re) +.15 E(viously been initialized.)-.25 E(The)133 350.4 Q F1(delete)2.934 E F0 +.434(functions return -1 on error)2.934 F 2.934(,s)-.4 G(etting)297.818 350.4 Q +F1(errno)2.934 E F0 2.934(,0o).18 G 2.934(ns)364.3 350.4 S .434 +(uccess, and 1 if the speci\214ed)376.124 350.4 R F1 -.1(ke)2.935 G(y)-.2 E F0 +.435(did not)2.935 F -.15(ex)133 362.4 S(ist in the \214le.).15 E +(int \(*fd\)\(const DB *db\);)108 379.2 Q 3.351(Ap)133 391.2 S .851 +(ointer to a function which returns a \214le descriptor representati)148.571 +391.2 R 1.15 -.15(ve o)-.25 H 3.35(ft).15 G .85(he underlying database.)430.53 +391.2 R(A)5.85 E .338(\214le descriptor referencing the same \214le will be re\ +turned to all processes which call)133 403.2 R F1(db_open)2.838 E F0 .339 +(with the)2.839 F(same)133 415.2 Q F1(\214le)3.376 E F0 3.376(name. This)3.376 +F .876(\214le descriptor may be safely used as an ar)3.376 F .876 +(gument to the)-.18 F F1(fcntl)3.376 E F0 .875(\(2\) and).51 F F1(\215oc)3.375 +E(k)-.2 E F0(\(2\)).67 E .99(locking functions.)133 427.2 R .99 +(The \214le descriptor is not necessarily associated with an)5.99 F 3.49(yo) +-.15 G 3.49(ft)453.98 427.2 S .99(he underlying \214les)463.58 427.2 R +(used by the access method.)133 439.2 Q(No \214le descriptor is a)5 E -.25(va) +-.2 G(ilable for in memory databases.).25 E(The)133 456 Q F1(fd)2.5 E F0 +(functions return -1 on error)2.5 E 2.5(,s)-.4 G(etting)278.68 456 Q F1(errno) +2.5 E F0 2.5(,a).18 G(nd the \214le descriptor on success.)335.8 456 Q +(int \(*get\)\(const DB *db, TXN *txnid,)108 472.8 Q(const DBT *k)183 484.8 Q +-.15(ey)-.1 G 2.5(,D)-.5 G(BT *data, u_int \215ags\);)259.87 484.8 Q 2.854(Ap) +133 496.8 S .354(ointer to a function which is the interf)148.074 496.8 R .354 +(ace for k)-.1 F -.15(ey)-.1 G .353(ed retrie).15 F -.25(va)-.25 G 2.853(lf).25 +G .353(rom the database.)397.995 496.8 R .353(The address and)5.353 F +(length of the data associated with the speci\214ed)133 508.8 Q F1 -.1(ke)2.5 G +(y)-.2 E F0(are returned in the structure referenced by)2.5 E F1(data)2.5 E F0 +(.).26 E(The)133 525.6 Q F1(txnid)3.316 E F0 .816 +(parameter contains a transaction ID returned from)3.316 F F1(txn_be)3.317 E +(gin)-.4 E F0 3.317(,i).24 G 3.317(ft)431.215 525.6 S .817 +(he \214le is being accessed)440.642 525.6 R +(under transaction protection, or NULL if transactions are not in ef)133 537.6 +Q(fect.)-.25 E(The)133 554.4 Q F1 -.1(ge)2.5 G(t).1 E F0 +(functions return -1 on error)2.5 E 2.5(,s)-.4 G(etting)283.02 554.4 Q F1 +(errno)2.5 E F0 2.5(,0o).18 G 2.5(ns)348.2 554.4 S(uccess, and 1 if the)359.59 +554.4 Q F1 -.1(ke)2.5 G(y)-.2 E F0 -.1(wa)2.5 G 2.5(sn).1 G(ot found.)476.83 +554.4 Q(int \(*put\)\(const DB *db, TXN *txnid,)108 571.2 Q(DBT *k)183 583.2 Q +-.15(ey)-.1 G 2.5(,c)-.5 G(onst DBT *data, u_int \215ags\);)233.48 583.2 Q 2.5 +(Ap)133 595.2 S(ointer to a function to store k)147.72 595.2 Q -.15(ey)-.1 G +(/data pairs in the database.).15 E(The)133 612 Q F1(txnid)3.317 E F0 .817 +(parameter contains a transaction ID returned from)3.317 F F1(txn_be)3.317 E +(gin)-.4 E F0 3.317(,i).24 G 3.316(ft)431.22 612 S .816 +(he \214le is being accessed)440.646 612 R +(under transaction protection, or NULL if transactions are not in ef)133 624 Q +(fect.)-.25 E(The parameter)133 640.8 Q F1<8d61>2.5 E(g)-.1 E F0 +(must be set to 0 or e)2.5 E(xactly one of the follo)-.15 E(wing v)-.25 E +(alues:)-.25 E(R_CURSOR)133 657.6 Q .448(Replace the k)158 669.6 R -.15(ey)-.1 +G .448(/data pair referenced by the cursor).15 F 5.449(.T)-.55 G .449 +(he cursor must ha)375.156 669.6 R .749 -.15(ve p)-.2 H(re).15 E .449 +(viously been ini-)-.25 F(tialized.)158 681.6 Q(4.4 Berk)72 732 Q(ele)-.1 E 2.5 +(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(3)535 +732 Q EP +%%Page: 4 20 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 119.01(DB_OPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 119.01(anual DB_OPEN\(3\))340.17 48 R(R_NOO)133 84 Q(VER)-.5 E +(WRITE)-.55 E(Enter the ne)158 96 Q 2.5(wk)-.25 G -.15(ey)220.69 96 S +(/data pair only if the k).15 E .3 -.15(ey d)-.1 H(oes not pre).15 E(viously e) +-.25 E(xist.)-.15 E .664(The def)133 112.8 R .664(ault beha)-.1 F .664 +(vior of the)-.2 F/F1 10/Times-Italic@0 SF(put)3.164 E F0 .664 +(functions is to enter the ne)3.164 F 3.164(wk)-.25 G -.15(ey)387.498 112.8 S +.663(/data pair).15 F 3.163(,r)-.4 G .663(eplacing an)443.534 112.8 R 3.163(yp) +-.15 G(re)503.03 112.8 Q(viously)-.25 E -.15(ex)133 124.8 S(isting k).15 E -.15 +(ey)-.1 G(.)-.5 E(The)133 141.6 Q F1(put)3.558 E F0 1.058 +(functions return -1 on error)3.558 F 3.559(,s)-.4 G(etting)291.089 141.6 Q F1 +(errno)3.559 E F0 3.559(,0o).18 G 3.559(ns)359.446 141.6 S 1.059 +(uccess, and 1 if the R_NOO)371.895 141.6 R(VER)-.5 E(WRITE)-.55 E F1<8d61>133 +153.6 Q(g)-.1 E F0 -.1(wa)2.5 G 2.5(ss).1 G(et and the k)172.24 153.6 Q .3 -.15 +(ey a)-.1 H(lready e).15 E(xists in the \214le.)-.15 E +(int \(*seq\)\(const DB *db, TXN *txnid,)108 170.4 Q(DBT *k)183 182.4 Q -.15 +(ey)-.1 G 2.5(,D)-.5 G(BT *data, u_int \215ags\);)236.26 182.4 Q 2.877(Ap)133 +194.4 S .377(ointer to a function which is the interf)148.097 194.4 R .377 +(ace for sequential retrie)-.1 F -.25(va)-.25 G 2.877(lf).25 G .377 +(rom the database.)415.194 194.4 R .376(The address)5.376 F .012 +(and length of the k)133 206.4 R .312 -.15(ey a)-.1 H .012 +(re returned in the structure referenced by).15 F F1 -.1(ke)2.512 G(y)-.2 E F0 +2.512(,a).32 G .012(nd the address and length of the)412.726 206.4 R +(data are returned in the structure referenced by)133 218.4 Q F1(data)2.5 E F0 +(.).26 E(The)133 235.2 Q F1(txnid)3.317 E F0 .817 +(parameter contains a transaction ID returned from)3.317 F F1(txn_be)3.317 E +(gin)-.4 E F0 3.317(,i).24 G 3.316(ft)431.22 235.2 S .816 +(he \214le is being accessed)440.646 235.2 R +(under transaction protection, or NULL if transactions are not in ef)133 247.2 +Q(fect.)-.25 E .721(Sequential k)133 264 R -.15(ey)-.1 G .721 +(/data pair retrie).15 F -.25(va)-.25 G 3.221(lm).25 G .721(ay be)277.884 264 R +.721(gin at an)-.15 F 3.221(yt)-.15 G .721 +(ime, and the logical position of the `)346.568 264 R(`cursor')-.74 E 3.222('i) +-.74 G(s)536.11 264 Q .947(not af)133 276 R .947(fected by calls to the)-.25 F +F1(del)3.447 E F0(,).51 E F1 -.1(ge)3.447 G(t).1 E F0(,).68 E F1(put)3.447 E F0 +3.446(,o).68 G(r)308.572 276 Q F1(sync)3.446 E F0 3.446 +(functions. Modi\214cations)3.446 F .946(to the database during a)3.446 F 2.091 +(sequential scan will be re\215ected in the scan, i.e. records inserted behind\ + the cursor will not be)133 288 R +(returned while records inserted in front of the cursor will be returned.)133 +300 Q(The parameter)133 316.8 Q F1<8d61>2.5 E(g)-.1 E F0(must be set to 0 or e) +2.5 E(xactly one of the follo)-.15 E(wing v)-.25 E(alues:)-.25 E(R_CURSOR)133 +333.6 Q .937(The data associated with the speci\214ed k)158 345.6 R 1.237 -.15 +(ey i)-.1 H 3.437(sr).15 G 3.437(eturned. This)348.546 345.6 R(dif)3.437 E .936 +(fers from the)-.25 F F1 -.1(ge)3.436 G(t).1 E F0 .936(functions in)3.436 F +(that it sets or initializes the cursor to the location of the k)158 357.6 Q .3 +-.15(ey a)-.1 H 2.5(sw).15 G(ell.)415.5 357.6 Q(R_FIRST)133 374.4 Q .835 +(The \214rst k)158 386.4 R -.15(ey)-.1 G .835(/data pair of the database is re\ +turned, and the cursor is set or initialized to refer).15 F(-)-.2 E(ence it.) +158 398.4 Q(R_NEXT)133 415.2 Q(Retrie)158 427.2 Q 1.015 -.15(ve t)-.25 H .715 +(he k).15 F -.15(ey)-.1 G .715(/data pair immediately after the cursor).15 F +5.715(.I)-.55 G 3.215(ft)391.91 427.2 S .714 +(he cursor is not yet set, this is the)401.235 427.2 R +(same as the R_FIRST \215ag.)158 439.2 Q(The)133 456 Q F1(seq)3.014 E F0 .514 +(functions return -1 on error)3.014 F 3.015(,s)-.4 G(etting)287.83 456 Q F1 +(errno)3.015 E F0 3.015(,0o).18 G 3.015(ns)354.555 456 S .515 +(uccess, and 1 if there are no k)366.46 456 R -.15(ey)-.1 G .515(/data pairs) +.15 F(less than or greater than the speci\214ed or current k)133 468 Q -.15(ey) +-.1 G(.)-.5 E(int \(*sync\)\(const DB *db, u_int \215ags\);)108 484.8 Q 3.291 +(Ap)133 496.8 S .791(ointer to a function to \215ush an)148.511 496.8 R 3.291 +(yc)-.15 G .791(ached information to disk.)286.388 496.8 R .79 +(If the database is in memory only)5.79 F(,)-.65 E(the)133 508.8 Q F1(sync)2.5 +E F0(function has no ef)2.5 E(fect and will al)-.25 E -.1(wa)-.1 G(ys succeed.) +.1 E(The parameter)133 525.6 Q F1<8d61>2.5 E(g)-.1 E F0 +(must be set to 0 or a v)2.5 E +(alue speci\214ed by an access method speci\214c manual page.)-.25 E(The)133 +542.4 Q F1(sync)2.5 E F0(functions return -1 on f)2.5 E(ailure, setting)-.1 E +F1(errno)2.5 E F0 2.5(,a).18 G(nd 0 on success.)352.76 542.4 Q/F2 9 +/Times-Bold@0 SF(ERR)72 559.2 Q(ORS)-.27 E F0(The)108 571.2 Q F1(db_open)4.548 +E F0 2.048(function may f)4.548 F 2.049(ail and set)-.1 F F1(errno)4.549 E F0 +2.049(for an)4.549 F 4.549(yo)-.15 G 4.549(ft)345.977 571.2 S 2.049 +(he errors speci\214ed for the library functions)356.636 571.2 R F1(open)108 +583.2 Q F0(\(2\),).24 E F1(malloc)2.5 E F0(\(3\) or the follo).31 E(wing:)-.25 +E([EFTYPE])108 600 Q 2.5<418c>133 612 S(le is incorrectly formatted.)148.28 612 +Q([EINV)108 628.8 Q(AL])-1.35 E 2.557(Ap)133 640.8 S .056 +(arameter has been speci\214ed \(hash function, recno pad byte etc.\))147.777 +640.8 R .056(that is incompatible with the cur)5.056 F(-)-.2 E .725 +(rent \214le speci\214cation or)133 652.8 R 3.225(,a\215)-.4 G .725 +(ag to a function which is not meaningful for the function \(for e)248.435 +652.8 R(xample,)-.15 E .763(use of the cursor without prior initialization\) o\ +r there is a mismatch between the v)133 664.8 R .763(ersion number of)-.15 F +(\214le and the softw)133 676.8 Q(are.)-.1 E(The)108 693.6 Q F1(close)2.913 E +F0 .413(functions may f)2.913 F .413(ail and set)-.1 F F1(errno)2.913 E F0 .413 +(for an)2.913 F 2.913(yo)-.15 G 2.913(ft)319.62 693.6 S .414 +(he errors speci\214ed for the library functions)328.643 693.6 R F1(close)2.914 +E F0(\(2\),).18 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q +99.315(ution August)-.2 F(1, 1995)2.5 E(4)535 732 Q EP +%%Page: 5 21 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 119.01(DB_OPEN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 119.01(anual DB_OPEN\(3\))340.17 48 R/F1 10/Times-Italic@0 SF -.37 +(re)108 84 S(ad).37 E F0(\(2\),).77 E F1(write)2.5 E F0(\(2\),).18 E F1(fr)2.5 +E(ee)-.37 E F0(\(3\), or).18 E F1(fsync)2.5 E F0(\(2\).).31 E(The)108 100.8 Q +F1(del)2.52 E F0(,).51 E F1 -.1(ge)2.52 G(t).1 E F0(,).68 E F1(put)2.52 E F0 +(and)2.52 E F1(seq)2.52 E F0 .02(functions may f)2.52 F .02(ail and set)-.1 F +F1(errno)2.52 E F0 .02(for an)2.52 F 2.52(yo)-.15 G 2.52(ft)376.3 100.8 S .02 +(he errors speci\214ed for the library func-)384.93 100.8 R(tions)108 112.8 Q +F1 -.37(re)2.5 G(ad).37 E F0(\(2\),).77 E F1(write)2.5 E F0(\(2\),).18 E F1(fr) +2.5 E(ee)-.37 E F0(\(3\) or).18 E F1(malloc)2.5 E F0(\(3\).).31 E(The)108 129.6 +Q F1(fd)2.5 E F0(functions will f)2.5 E(ail and set)-.1 E F1(errno)2.5 E F0 +(to ENOENT for in memory databases.)2.5 E(The)108 146.4 Q F1(sync)2.5 E F0 +(functions may f)2.5 E(ail and set)-.1 E F1(errno)2.5 E F0(for an)2.5 E 2.5(yo) +-.15 G 2.5(ft)312.71 146.4 S(he errors speci\214ed for the library function) +321.32 146.4 Q F1(fsync)2.5 E F0(\(2\).).31 E/F2 9/Times-Bold@0 SF(SEE ALSO)72 +163.2 Q F1(db_btr)108 175.2 Q(ee)-.37 E F0(\(3\),).18 E F1(db_hash)2.5 E F0 +(\(3\),).28 E F1(db_loc)2.5 E(k)-.2 E F0(\(3\),).67 E F1(db_lo)2.5 E(g)-.1 E F0 +(\(3\),).22 E F1(db_mpool)2.5 E F0(\(3\),).51 E F1(db_r)2.5 E(ecno)-.37 E F0 +(\(3\),).18 E F1(db_txn)2.5 E F0(\(3\)).24 E F2 -.09(BU)72 192 S(GS).09 E F0 +.106(The name DBT is a mnemonic for `)108 204 R .106(`data base thang')-.74 F +.106(', and w)-.74 F .107(as used because noone could think of a reason-)-.1 F +(able name that w)108 216 Q(asn')-.1 E 2.5(ta)-.18 G(lready in use some)202.14 +216 Q(where else.)-.25 E(The)108 232.8 Q F1(fd)2.5 E F0(function interf)2.5 E +(ace is a kluge, and will be deleted in a future v)-.1 E(ersion of the interf) +-.15 E(ace.)-.1 E(Only big and little endian byte order is supported.)108 249.6 +Q(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(5)535 732 Q EP +%%Page: 1 22 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 111.23(DB_RECNO\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 111.23(anual DB_RECNO\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA) +72 84 S(ME).18 E F0(db_recno \255 record number database access method)108 96 Q +F1(DESCRIPTION)72 112.8 Q F0(speci\214c details of the recno access method.)108 +124.8 Q F1 -.495(AC)72 141.6 S(CESS METHOD SPECIFIC INFORMA).495 E(TION)-.855 E +F0 2.497(The recno access method speci\214c data structure pro)108 153.6 R +2.497(vided to)-.15 F/F2 10/Times-Italic@0 SF(db_open)4.997 E F0 2.497 +(is typedef)4.997 F 3.497 -.5('d a).55 H 2.497(nd named REC-).5 F 2.765 +(NOINFO. A)108 165.6 R .265(RECNOINFO structure has at least the follo)2.765 F +.266(wing \214elds, which may be initialized before call-)-.25 F(ing)108 177.6 +Q F2(db_open)2.5 E F0(:).24 E(u_int8_t b)108 194.4 Q -.25(va)-.15 G(l;).25 E +.793(The delimiting byte to be used to mark the end of a record for v)133 206.4 +R .793(ariable-length records, and the pad)-.25 F .386(character for \214x)133 +218.4 R .387(ed-length records.)-.15 F .387(If no v)5.387 F .387 +(alue is speci\214ed, ne)-.25 F .387(wlines \(`)-.25 F(`\\n')-.74 E .387 +('\) are used to mark the end)-.74 F(of v)133 230.4 Q +(ariable-length records and \214x)-.25 E +(ed-length records are padded with spaces.)-.15 E(char *bfname;)108 247.2 Q +1.152(The recno access method stores the in-memory copies of its records in a \ +btree.)133 259.2 R 1.152(If bfname is non-)6.152 F .35(NULL, it speci\214es th\ +e name of the btree \214le, as if speci\214ed as the \214le name for a)133 +271.2 R F2(db_open)2.851 E F0 .351(of a btree)2.851 F(\214le.)133 283.2 Q +(u_int cachesize;)108 300 Q 3.847(As)133 312 S 1.347 +(uggested maximum size, in bytes, of the memory cache.)147.957 312 R 1.347 +(This v)6.347 F 1.347(alue is)-.25 F/F3 10/Times-Bold@0 SF(only)3.847 E F0 +(advisory)3.847 E 3.846(,a)-.65 G 1.346(nd the)513.934 312 R .693 +(access method will allocate more memory rather than f)133 324 R 3.193(ail. If) +-.1 F F2(cac)3.193 E(hesize)-.15 E F0 3.193(is 0)3.193 F .693 +(\(no size is speci\214ed\) a)3.193 F(def)133 336 Q(ault size is used.)-.1 E +(u_long \215ags;)108 352.8 Q(The \215ag v)133 364.8 Q(alue is speci\214ed by) +-.25 E F2(or)2.5 E F0('ing an).73 E 2.5(yo)-.15 G 2.5(ft)302.2 364.8 S +(he follo)310.81 364.8 Q(wing v)-.25 E(alues:)-.25 E(R_FIXEDLEN)133 381.6 Q +1.49(The records are \214x)158 393.6 R 1.489(ed-length, not byte delimited.) +-.15 F 1.489(The structure element)6.489 F F2 -.37(re)3.989 G(clen).37 E F0 +1.489(speci\214es the)3.989 F .487 +(length of the record, and the structure element)158 405.6 R F2(bval)2.987 E F0 +.488(is used as the pad character)2.988 F 5.488(.A)-.55 G .788 -.15(ny r) +495.232 405.6 T(ecords,).15 E(inserted into the database, that are less than) +158 417.6 Q F2 -.37(re)2.5 G(clen).37 E F0 +(bytes long are automatically padded.)2.5 E(R_NOKEY)133 434.4 Q 1.146 +(In the interf)158 446.4 R 1.146(ace speci\214ed by)-.1 F F2(db_open)3.646 E F0 +3.646(,t).24 G 1.146(he sequential record retrie)320.816 446.4 R -.25(va)-.25 G +3.646<6c8c>.25 G 1.145(lls in both the caller')449.31 446.4 R(s)-.55 E -.1(ke) +158 458.4 S 4.795(ya)-.05 G 2.295(nd data structures.)181.425 458.4 R 2.295 +(If the R_NOKEY \215ag is speci\214ed, the)7.295 F F2(cur)4.795 E(sor)-.1 E F0 +2.295(functions are not)4.795 F .621(required to \214ll in the k)158 470.4 R +.921 -.15(ey s)-.1 H 3.121(tructure. This).15 F .621 +(permits applications to retrie)3.121 F .92 -.15(ve r)-.25 H .62 +(ecords at the end of).15 F(\214les without reading all of the interv)158 482.4 +Q(ening records.)-.15 E(R_SN)133 499.2 Q(APSHO)-.35 E(T)-.4 E .029 +(This \215ag requires that a snapshot of the \214le be tak)158 511.2 R .029 +(en when)-.1 F F2(db_open)2.529 E F0 .029(is called, instead of permit-)2.529 F +(ting an)158 523.2 Q 2.5(yu)-.15 G +(nmodi\214ed records to be read from the original \214le.)197.85 523.2 Q +(int lorder;)108 540 Q .65(The byte order for inte)133 552 R .65 +(gers in the stored database metadata.)-.15 F .65 +(The number should represent the order)5.65 F .748(as an inte)133 564 R .749 +(ger; for e)-.15 F .749(xample, big endian order w)-.15 F .749 +(ould be the number 4,321.)-.1 F(If)5.749 E F2(lor)3.249 E(der)-.37 E F0 .749 +(is 0 \(no order is)3.249 F(speci\214ed\) the current host order is used.)133 +576 Q(u_int psize;)108 592.8 Q .284(The recno access method stores the in-memo\ +ry copies of its records in a btree.)133 604.8 R .284(This v)5.284 F .283 +(alue is the size)-.25 F .297 +(\(in bytes\) of the pages used for nodes in that tree.)133 616.8 R(If)5.297 E +F2(psize)2.797 E F0 .297(is 0 \(no page size is speci\214ed\) a page size)2.797 +F(is chosen based on the underlying \214le system I/O block size.)133 628.8 Q +(See)5 E F2(btr)2.5 E(ee)-.37 E F0(\(3\) for more information.).18 E +(size_t reclen;)108 645.6 Q(The length of a \214x)133 657.6 Q +(ed-length record.)-.15 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib) +132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 23 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 111.23(DB_RECNO\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 111.23(anual DB_RECNO\(3\))340.17 48 R/F1 9/Times-Bold@0 SF +(DB OPERA)72 84 Q(TIONS)-.855 E F0 .972(The data part of the k)108 96 R -.15 +(ey)-.1 G .971(/data pair used by the recno access method is the same as other\ + access methods.).15 F .198(The k)108 108 R .498 -.15(ey i)-.1 H 2.698(sd).15 G +(if)157.504 108 Q 2.698(ferent. The)-.25 F/F2 10/Times-Italic@0 SF(data)2.698 E +F0 .198(\214eld of the k)2.698 F .499 -.15(ey s)-.1 H .199 +(hould be a pointer to a memory location of type).15 F F2 -.37(re)2.699 G +(cno_t).37 E F0 2.699(,a).68 G(s)536.11 108 Q .506(de\214ned in the include \214le.)-.4 F .506(This type is normally the lar)5.506 F +.506(gest unsigned inte)-.18 F .506(gral type a)-.15 F -.25(va)-.2 G .505 +(ilable to the).25 F 2.5(implementation. The)108 132 R F2(size)2.5 E F0 +(\214eld of the k)2.5 E .3 -.15(ey s)-.1 H(hould be the size of that type.).15 +E 1.944(The record number data structure is either v)108 148.8 R 1.944 +(ariable or \214x)-.25 F 1.944 +(ed-length records stored in a \215at-\214le format,)-.15 F 1.856 +(accessed by the logical record number)108 160.8 R 6.856(.T)-.55 G 1.856(he e) +285.206 160.8 R 1.856(xistence of record number \214v)-.15 F 4.356(er)-.15 G +1.856(equires the e)440.442 160.8 R 1.856(xistence of)-.15 F .875 +(records one through four)108 172.8 R 3.375(,a)-.4 G .875 +(nd the deletion of record number one causes record number \214v)219.68 172.8 R +3.376(et)-.15 G 3.376(ob)489.928 172.8 S 3.376(er)503.304 172.8 S(enum-)514.45 +172.8 Q .283(bered to record number four)108 184.8 R 2.783(,a)-.4 G 2.783(sw) +231.195 184.8 S .283(ell as the cursor)245.088 184.8 R 2.783(,i)-.4 G 2.783(fp) +316.64 184.8 S .282(ositioned after record number one, to shift do)327.753 +184.8 R .282(wn one)-.25 F 3.18(record. The)108 196.8 R .68 +(creation of record number \214v)3.18 F 3.18(ew)-.15 G .681 +(hen records one through four do not e)295.05 196.8 R .681 +(xist causes the logical)-.15 F(creation of them with zero-length data.)108 +208.8 Q .372(Because there is no meta-data associated with the underlying recn\ +o access method \214les, an)108 225.6 R 2.872(yc)-.15 G .372(hanges made) +487.698 225.6 R .191(to the def)108 237.6 R .191(ault v)-.1 F .191 +(alues \(e.g. \214x)-.25 F .192(ed record length or byte separator v)-.15 F +.192(alue\) must be e)-.25 F .192(xplicitly speci\214ed each time)-.15 F +(the \214le is opened.)108 249.6 Q 1.037(The functions returned by)108 266.4 R +F2(db_open)3.537 E F0 1.036(for the btree access method are as described in) +3.536 F F2(db_open)3.536 E F0 1.036(\(3\), with the).24 F(follo)108 278.4 Q +(wing e)-.25 E(xceptions and additions:)-.15 E 5.28(type The)108 295.2 R +(type is DB_RECNO.)2.5 E 9.72(put Using)108 312 R(the)2.558 E F2(put)2.558 E F0 +(interf)2.559 E .059(ace to create a ne)-.1 F 2.559(wr)-.25 G .059 +(ecord will cause the creation of multiple, empty records if the)293.07 312 R +(record number is more than one greater than the lar)133 324 Q +(gest record currently in the database.)-.18 E(The)133 340.8 Q F2(put)2.5 E F0 +(function tak)2.5 E(es the follo)-.1 E(wing additional \215ags:)-.25 E +(R_IAFTER)133 357.6 Q 1.225 +(Append the data immediately after the data referenced by)158 369.6 R F2 -.1 +(ke)3.724 G(y)-.2 E F0 3.724(,c).32 G 1.224(reating a ne)425.354 369.6 R 3.724 +(wk)-.25 G -.15(ey)490.046 369.6 S 1.224(/data pair).15 F(.)-.55 E +(The record number of the appended k)158 381.6 Q -.15(ey)-.1 G +(/data pair is returned in the).15 E F2 -.1(ke)2.5 G(y)-.2 E F0(structure.)2.5 +E(R_IBEFORE)133 398.4 Q 1.343 +(Insert the data immediately before the data referenced by)158 410.4 R F2 -.1 +(ke)3.844 G(y)-.2 E F0 3.844(,c).32 G 1.344(reating a ne)424.874 410.4 R 3.844 +(wk)-.25 G -.15(ey)489.926 410.4 S 1.344(/data pair).15 F(.)-.55 E +(The record number of the inserted k)158 422.4 Q -.15(ey)-.1 G +(/data pair is returned in the).15 E F2 -.1(ke)2.5 G(y)-.2 E F0(structure.)2.5 +E(R_SETCURSOR)133 439.2 Q(Store the k)158 451.2 Q -.15(ey)-.1 G(/data pair).15 +E 2.5(,s)-.4 G +(etting or initializing the position of the cursor to reference it.)256.5 451.2 +Q 9.17(seq The)108 468 R F2(seq)2.5 E F0(function tak)2.5 E(es the follo)-.1 E +(wing additional \215ags:)-.25 E(R_LAST)133 484.8 Q .04(The last k)158 496.8 R +-.15(ey)-.1 G .04(/data pair of the database is returned, and the cursor is se\ +t or initialized to reference).15 F(it.)158 508.8 Q(R_PREV)133 525.6 Q(Retrie) +158 537.6 Q .59 -.15(ve t)-.25 H .29(he k).15 F -.15(ey)-.1 G .29 +(/data pair immediately before the cursor).15 F 5.29(.I)-.55 G 2.79(ft)395.73 +537.6 S .29(he cursor is not yet set, this is the)404.63 537.6 R +(same as the R_LAST \215ag.)158 549.6 Q .749 +(If the database \214le is a character special \214le and no complete k)133 +566.4 R -.15(ey)-.1 G .748(/data pairs are currently a).15 F -.25(va)-.2 G +(ilable,).25 E(the)133 578.4 Q F2(seq)2.5 E F0(function returns 2.)2.5 E 4.17 +(sync The)108 595.2 R F2(sync)2.5 E F0(function tak)2.5 E(es the follo)-.1 E +(wing additional \215ag:)-.25 E(R_RECNOSYNC)133 612 Q .643 +(This \215ag causes the)158 624 R F2(sync)3.143 E F0 .644 +(function to apply to the btree \214le which underlies the recno \214le, not) +3.143 F .09(the recno \214le itself.)158 636 R .09(\(See the)5.09 F F2(bfname) +2.59 E F0 .09(\214eld of RECNOINFO structure, abo)2.59 F -.15(ve)-.15 G 2.59 +(,f).15 G .09(or more informa-)470.95 636 R(tion.\))158 648 Q F1(ERR)72 664.8 Q +(ORS)-.27 E F0(The)108 676.8 Q F2 -.37(re)3.731 G(cno).37 E F0 1.231 +(access method functions may f)3.731 F 1.231(ail and set)-.1 F F2(errno)3.731 E +F0 1.231(for an)3.731 F 3.731(yo)-.15 G 3.731(ft)392.652 676.8 S 1.231 +(he errors speci\214ed for the library)402.493 676.8 R(function)108 688.8 Q F2 +(db_open)2.5 E F0(\(3\) or the follo).24 E(wing:)-.25 E(4.4 Berk)72 732 Q(ele) +-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E +(2)535 732 Q EP +%%Page: 3 24 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 111.23(DB_RECNO\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 111.23(anual DB_RECNO\(3\))340.17 48 R([EINV)108 84 Q(AL])-1.35 E +(An attempt w)133 96 Q(as made to add a record to a \214x)-.1 E +(ed-length database that w)-.15 E(as too lar)-.1 E(ge to \214t.)-.18 E/F1 9 +/Times-Bold@0 SF(SEE ALSO)72 112.8 Q/F2 10/Times-Italic@0 SF(db_btr)108 124.8 Q +(ee)-.37 E F0(\(3\),).18 E F2(db_hash)2.5 E F0(\(3\),).28 E F2(db_loc)2.5 E(k) +-.2 E F0(\(3\),).67 E F2(db_lo)2.5 E(g)-.1 E F0(\(3\),).22 E F2(db_mpool)2.5 E +F0(\(3\),).51 E F2(db_open)2.5 E F0(\(3\),).24 E F2(db_txn)2.5 E F0(\(3\)).24 E +F2 2.755(Document Pr)108 148.8 R 2.755 +(ocessing in a Relational Database System)-.45 F F0 5.254(,M).32 G 2.754 +(ichael Stonebrak)362.134 148.8 R(er)-.1 E 5.254(,H)-.4 G 2.754(eidi Stettner) +454.062 148.8 R 5.254(,J)-.4 G(oseph)516.67 148.8 Q +(Kalash, Antonin Guttman, Nadene L)108 160.8 Q +(ynn, Memorandum No. UCB/ERL M82/32, May 1982.)-.55 E F1 -.09(BU)72 177.6 S(GS) +.09 E F0(The)108 189.6 Q F2(sync)3.616 E F0(function')3.616 E 3.616(sR)-.55 G +1.116(_RECNOSYNC interf)198.838 189.6 R 1.117 +(ace is a kluge, and will be deleted in a future v)-.1 F 1.117(ersion of the) +-.15 F(interf)108 201.6 Q(ace.)-.1 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G +(istrib)132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(3)535 732 Q EP +%%Page: 1 25 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_TXN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_TXN\(3\))340.17 48 R/F1 9/Times-Bold@0 SF -.18(NA)72 +84 S(ME).18 E F0(db_txn \255 transaction management functions)108 96 Q F1 +(SYNOPSIS)72 112.8 Q/F2 10/Times-Bold@0 SF(#include )-.4 E +(#include )108 136.8 Q(int)108 160.8 Q(txn_cr)108 172.8 Q +(eate\(const char *path, mode_t mode, u_int maxtxns, u_int \215ags\);)-.18 E +(TXNMGR *)108 196.8 Q(txn_open\(const char *path, DBT *logp, LOCK_T)108 208.8 Q +(ABLE_T *lockp,)-.9 E(int \(*r)158 220.8 Q(eco)-.18 E -.1(ve)-.1 G +(r\)\(DBT *lsn, DBT *log_entry).1 E 2.5(,i)-.55 G(nt isundo\)\);)340.11 220.8 Q +(TXN *)108 244.8 Q(txn_begin\(TXNMGR *txnp\);)108 256.8 Q(int)108 280.8 Q +(txn_commit\(TXN *tid\);)108 292.8 Q(int)108 316.8 Q(txn_pr)108 328.8 Q(epar) +-.18 E(e\(TXN *tid\);)-.18 E(int)108 352.8 Q(txn_abort\(TXN *tid\);)108 364.8 Q +(int)108 388.8 Q(txn_close\(TXNMGR *txnp\);)108 400.8 Q(int)108 424.8 Q +(txn_unlink\(const char *path, int f)108 436.8 Q(or)-.25 E(ce\);)-.18 E F1 +(DESCRIPTION)72 453.6 Q F0(speci\214c details of the transaction support.)108 +465.6 Q/F3 10/Times-Italic@0 SF(Db_txn)108 482.4 Q F0 .034 +(is the library interf)2.534 F .034(ace that pro)-.1 F .034 +(vides transaction semantics.)-.15 F .034(Full transaction support is pro)5.034 +F .034(vided by a)-.15 F .722(collection of modules that pro)108 494.4 R .723 +(vide well de\214ned interf)-.15 F .723 +(aces to the services required for transaction process-)-.1 F 3.488(ing. These) +108 506.4 R .988(services are reco)3.488 F -.15(ve)-.15 G .988(ry \(see).15 F +F3(db_lo)3.488 E(g)-.1 E F0 .988(\(3\)\), concurrenc).22 F 3.488(yc)-.15 G .988 +(ontrol \(see)371.864 506.4 R F3(db_loc)3.487 E(k)-.2 E F0 .987 +(\(3\)\), and the manage-).67 F 2.201(ment of shared data \(see)108 518.4 R F3 +(db_mpool)4.701 E F0 4.701(\(3\)\). T).51 F 2.202 +(ransaction semantics can be applied to the access methods)-.35 F(described in) +108 530.4 Q F3(db)2.5 E F0(\(3\) through function call parameters.).23 E .629(\ +The model intended for transactional use \(and that is used by the access meth\ +ods\) is that write-ahead log-)108 547.2 R .047(ging is pro)108 559.2 R .047 +(vided by)-.15 F F3(db_lo)2.547 E(g)-.1 E F0 .047 +(\(3\) to record both before- and after).22 F .048(-image logging.)-.2 F .048 +(Locking follo)5.048 F .048(ws a tw)-.25 F(o-phase)-.1 E +(protocol and is implemented by)108 571.2 Q F3(db_loc)2.5 E(k)-.2 E F0(\(3\).) +.67 E .549(The function)108 588 R F3(txn_cr)3.049 E(eate)-.37 E F0 .549 +(creates and initializes the transaction re)3.049 F .548 +(gion identi\214ed by the)-.15 F F3(path)3.048 E F0(directory)3.048 E 5.548(.T) +-.65 G(his)528.33 588 Q .572(directory must already e)108 600 R .572(xist when) +-.15 F F3(txn_cr)3.072 E(eate)-.37 E F0 .572(is called.)3.072 F .572 +(If the transaction re)5.572 F .572(gion identi\214ed by)-.15 F F3(path)3.072 E +F0(already)3.072 E -.15(ex)108 612 S 1.78(ists, then).15 F F3(txn_cr)4.28 E +(eate)-.37 E F0 1.78(returns success without further action.)4.28 F 1.78 +(The \214les associated with the transaction)6.78 F(re)108 624 Q .293 +(gion are created in the directory speci\214ed by)-.15 F F3(path)2.793 E F0 +5.293(.\().28 G .293(The group of the created \214les is based on the system) +327.657 624 R .781(and directory def)108 636 R .781 +(aults, and is not further speci\214ed by)-.1 F F3(txn_cr)3.281 E(eate)-.37 E +F0 3.281(.\) All).18 F .78(\214les created by)3.28 F F3(txn_cr)3.28 E(eate)-.37 +E F0 .78(are cre-)3.28 F .048(ated with mode)108 648 R F3(mode)2.548 E F0 .049 +(\(as described in)2.548 F F3 -.15(ch)2.549 G(mod).15 E F0 .049 +(\(2\)\) and modi\214ed by the process' umask v).77 F .049(alue \(see)-.25 F F3 +(umask)2.549 E F0(\(2\)\).).67 E(An)108 660 Q 2.5(yn)-.15 G(ecessary)132.57 660 +Q 2.5(,a)-.65 G(ssociated log and lock re)175.23 660 Q +(gions are created as well \(see)-.15 E F3(db_lo)2.5 E(g)-.1 E F0(\(3\) and).22 +E F3(db_loc)2.5 E(k)-.2 E F0(\(3\)\).).67 E(The)108 676.8 Q F3(maxtxns)4.191 E +F0(ar)4.191 E 1.691(gument speci\214es the maximum number of simultaneous tran\ +sactions that are supported.)-.18 F .229 +(This bounds the size of backing \214les and is used to deri)108 688.8 R .529 +-.15(ve l)-.25 H .229(imits for the size of the lock re).15 F .229 +(gion and log\214les.)-.15 F(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib) +132.57 732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(1)535 732 Q EP +%%Page: 2 26 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_TXN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_TXN\(3\))340.17 48 R(When there are more than)108 84 +Q/F1 10/Times-Italic@0 SF(maxtxns)2.5 E F0(concurrent transactions, calls to) +2.5 E F1(txn_be)2.5 E(gin)-.4 E F0(may f)2.5 E(ail.)-.1 E(Def)108 100.8 Q .847 +(ault locking and logging protocols are pro)-.1 F .846 +(vided only if the backing \214les e)-.15 F 3.346(xist. If)-.15 F .846 +(the backing \214les do)3.346 F 1.433(not e)108 112.8 R 1.433(xist, the)-.15 F +F1<8d61>3.933 E(gs)-.1 E F0 1.434 +(parameter must indicate both a logging mode and locking mode speci\214ed by) +3.933 F F1(or)3.934 E F0('ing).73 E(together at most one \215ag from each of t\ +he TXN_LOCK and TXN_LOG classes as follo)108 124.8 Q(ws:)-.25 E(TXN_LOCK_2PL) +108 141.6 Q(Use tw)133 153.6 Q(o-phase locking.)-.1 E(TXN_LOCK_OPTIMISTIC)108 +170.4 Q(Use optimistic locking \(not currently implemented\).)133 182.4 Q +(TXN_LOG_REDO)108 199.2 Q(Pro)133 211.2 Q +(vide redo-only logging \(not currently implemented\).)-.15 E(TXN_LOG_UNDO)108 +228 Q(Pro)133 240 Q(vide undo-only logging \(not currently implemented\).)-.15 +E(TXN_LOG_UNDOREDO)108 256.8 Q(Pro)133 268.8 Q +(vide undo/redo write-ahead logging.)-.15 E(The function)108 285.6 Q F1(txn_cr) +2.5 E(eate)-.37 E F0(returns -1 on f)2.5 E(ailure, setting)-.1 E F1(errno)2.5 E +F0 2.5(,a).18 G(nd 0 on success.)351.83 285.6 Q 1.892(The function)108 302.4 R +F1(txn_open)4.392 E F0 1.892(returns a pointer to the transaction re)4.392 F +1.892(gion identi\214ed by)-.15 F F1(path)4.392 E F0 4.392(,w).28 G 1.892 +(hich must ha)476.016 302.4 R -.15(ve)-.2 G .239 +(already been created by a call to)108 314.4 R F1(txn_cr)2.739 E(eate)-.37 E F0 +5.239(.T).18 G .239(he process must ha)296.88 314.4 R .539 -.15(ve p)-.2 H .239 +(ermission to read and write \214les with).15 F -.25(ow)108 326.4 S .327 +(ners, groups and permissions as described for).25 F F1(txn_cr)2.826 E(eate) +-.37 E F0 5.326(.T).18 G(he)362.624 326.4 Q F1(txn_open)2.826 E F0 .326 +(function returns NULL on f)2.826 F(ail-)-.1 E(ure, setting)108 338.4 Q F1 +(errno)2.5 E F0(.).18 E(The)108 355.2 Q F1 -.37(re)3.181 G(co).37 E(ver)-.1 E +F0(ar)3.181 E .681(gument speci\214es a function that is called by)-.18 F F1 +(txn_abort)3.181 E F0 .682(during transaction abort.)3.182 F .682(This func-) +5.682 F(tion tak)108 367.2 Q(es three ar)-.1 E(guments:)-.18 E 10.83(lsn A)108 +384 R(log sequence number \(LSN\).)2.5 E(log_entry)108 400.8 Q 2.5(Al)133 412.8 +S(og record.)145.5 412.8 Q(isundo)108 429.6 Q(An undo \215ag set to 0 if the o\ +peration is a redo and set to 1 if the operation an undo.)133 441.6 Q 1.498 +(As discussed in the)108 458.4 R F1(db_lo)3.998 E 3.998(g\()-.1 G(3\))228.44 +458.4 Q F0 1.497(manual page, the application is responsible for pro)3.997 F +1.497(viding an)-.15 F 3.997(yn)-.15 G(ecessary)506.13 458.4 Q .486 +(structure to the log record.)108 470.4 R -.15(Fo)5.486 G 2.986(re).15 G .487 +(xample, the application must understand what part of the log record is an) +242.256 470.4 R(operation code, what part is redo information, and what part i\ +s undo information.)108 482.4 Q(The)108 499.2 Q F1(txn_be)2.785 E(gin)-.4 E F0 +.285(function creates a ne)2.785 F 2.784(wt)-.25 G .284 +(ransaction in the designated transaction manager)264.018 499.2 R 2.784(,r)-.4 +G .284(eturning a pointer)468.332 499.2 R +(to a TXN that uniquely identi\214es it.)108 511.2 Q(The)108 528 Q F1 +(txn_commit)2.615 E F0 .115(function ends the transaction speci\214ed by the) +2.615 F F1(tid)2.615 E F0(ar)2.615 E 2.615(gument. An)-.18 F 2.615(yl)-.15 G +.115(ocks held by the transac-)440.12 528 R(tion are released.)108 540 Q +(If logging is enabled, a commit log record is written and \215ushed to disk.)5 +E(The)108 556.8 Q F1(txn_abort)2.889 E F0 .389 +(function causes an abnormal termination of the transaction.)2.889 F .388 +(If logging is enabled, the log is)5.389 F 2.312(played backw)108 568.8 R 2.312 +(ards and an)-.1 F 4.812(yr)-.15 G(eco)228.628 568.8 Q -.15(ve)-.15 G 2.312 +(ry operations are initiated through the).15 F F1 -.37(re)4.813 G(co).37 E(ver) +-.1 E F0 2.313(function speci\214ed to)4.813 F F1(txn_open)108 580.8 Q F0 5(.A) +.24 G(fter reco)159.62 580.8 Q -.15(ve)-.15 G +(ry is completed, all locks held by the transaction are released.).15 E(The)108 +597.6 Q F1(txn_close)3.824 E F0 1.323 +(function detaches a process from the transaction en)3.823 F 1.323 +(vironment speci\214ed by the TXNMGR)-.4 F(pointer)108 609.6 Q 5.261(.A)-.55 G +.261(ll mapped re)150.761 609.6 R .261(gions are unmapped and an)-.15 F 2.761 +(ya)-.15 G .262(llocated resources are freed.)323.638 609.6 R(An)5.262 E 2.762 +(yu)-.15 G .262(ncommitted trans-)466.688 609.6 R(actions are aborted.)108 +621.6 Q .69(The function)108 638.4 R F1(txn_unlink)3.19 E F0(destro)3.19 E .69 +(ys the transaction re)-.1 F .69(gion identi\214ed by the directory)-.15 F F1 +(path)3.19 E F0 3.19(,r).28 G(emo)472.1 638.4 Q .69(ving all \214les)-.15 F +1.78(used to implement the transaction re)108 650.4 R 4.28(gion. \(The)-.15 F +(directory)4.28 E F1(path)4.28 E F0 1.78(is not remo)4.28 F -.15(ve)-.15 G 4.28 +(d.\) If).15 F 1.78(there are processes)4.28 F .553(which ha)108 662.4 R .853 +-.15(ve c)-.2 H(alled).15 E F1(txn_open)3.052 E F0 .552(without calling)3.052 F +F1(txn_close)3.052 E F0 .552 +(\(i.e., there are processes currently using the transac-)3.052 F .135(tion re) +108 674.4 R(gion\),)-.15 E F1(txn_unlink)2.635 E F0 .135(will f)2.635 F .135 +(ail without further action, unless the force \215ag is set, in which case)-.1 +F F1(txn_unlink)2.636 E F0 1.67(will attempt to delete the transaction re)108 +686.4 R 1.67(gion \214les re)-.15 F -.05(ga)-.15 G 1.67(rdless of an).05 F 4.17 +(yp)-.15 G 1.67(rocesses still using the transaction)397.22 686.4 R(4.4 Berk)72 +732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315(ution August)-.2 F +(1, 1995)2.5 E(2)535 732 Q EP +%%Page: 3 27 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_TXN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_TXN\(3\))340.17 48 R(re)108 84 Q 2.996(gion. An)-.15 +F 2.996(ya)-.15 G .496(ccesses to a remo)165.902 84 R -.15(ve)-.15 G 2.997(dt) +.15 G .497(ransaction re)257.007 84 R .497(gion will lik)-.15 F .497 +(ely result in une)-.1 F .497(xpected beha)-.15 F(vior)-.2 E 5.497(.T)-.55 G +.497(he func-)506.463 84 R(tion)108 96 Q/F1 10/Times-Italic@0 SF(txn_unlink)2.5 +E F0(returns -1 on f)2.5 E(ailure, setting)-.1 E F1(errno)2.5 E F0 2.5(,a).18 G +(nd 0 on success.)316.39 96 Q .51(In the case of catastrophic or system f)108 +112.8 R .51(ailure, it is possible to clean up a transaction re)-.1 F .51 +(gion by remo)-.15 F .51(ving all)-.15 F .34 +(of the \214les in the directory speci\214ed to the)108 124.8 R F1(txn_cr)2.841 +E(eate)-.37 E F0 .341(function, as transaction re)2.841 F .341 +(gion \214les are ne)-.15 F -.15(ve)-.25 G 2.841(rc).15 G(reated)515.57 124.8 Q +(in an)108 136.8 Q 2.5(yd)-.15 G(irectory other than the one speci\214ed to) +140.07 136.8 Q F1(txn_cr)2.5 E(eate)-.37 E F0(.).18 E(The)108 153.6 Q F1 +(txn_pr)3.42 E(epar)-.37 E(e)-.37 E F0 .92(function initiates the be)3.42 F +.919(ginning of a tw)-.15 F 3.419(op)-.1 G .919(hase commit.)352.206 153.6 R +.919(In a distrib)5.919 F .919(uted transaction, the)-.2 F .185 +(prepare directi)108 165.6 R .485 -.15(ve s)-.25 H .185 +(hould be issued to all participating transaction managers.).15 F .185 +(Each manager must tak)5.185 F 2.685(ew)-.1 G(hat-)524.45 165.6 Q -2.15 -.25 +(ev e)108 177.6 T 2.5(ra).25 G +(ction is necessary to guarantee that a future call to)131.75 177.6 Q F1 +(txn_commit)2.5 E F0(on the speci\214ed)2.5 E F1(tid)2.5 E F0(will succeed.)2.5 +E/F2 9/Times-Bold@0 SF(SYSTEM INTEGRA)72 194.4 Q(TION)-.855 E F0 .28 +(This model can be applied to data bases other than the pro)108 206.4 R .279 +(vided access methods.)-.15 F -.15(Fo)5.279 G 2.779(re).15 G .279 +(xample, consider an)459.182 206.4 R .15(application that pro)108 218.4 R .15 +(vides transaction semantics to data stored in re)-.15 F .15 +(gular \214les accessed using the)-.15 F F1 -.37(re)2.65 G(ad).37 E F0 .15 +(\(2\) and).77 F F1(write)108 230.4 Q F0 .708(\(2\) system calls.).18 F .707 +(The operations for which transaction protection is desired are brack)5.708 F +.707(eted by calls to)-.1 F F1(txn_be)108 242.4 Q(gin)-.4 E F0(and)2.5 E F1 +(txn_commit)2.5 E F0(.).68 E .606 +(Before data are referenced, a call is made to the lock manager)108 259.2 R(,) +-.4 E F1(db_loc)3.106 E(k)-.2 E F0 3.106(,f).67 G .606 +(or a lock of the appropriate type)408.064 259.2 R .719 +(\(e.g. read\) on the object being lock)108 271.2 R 3.218(ed. The)-.1 F .718 +(object might be a page in the \214le, a byte, a range of bytes, or)3.218 F +.572(some k)108 283.2 R -.15(ey)-.1 G 5.572(.B)-.5 G .573 +(efore a write is performed, the application mak)160.464 283.2 R .573 +(es a call to the log manager)-.1 F(,)-.4 E F1(db_lo)3.073 E(g)-.1 E F0 3.073 +(,t).22 G 3.073(or)506.387 283.2 S(ecord)517.79 283.2 Q .522 +(enough information to redo the operation in case of f)108 295.2 R .522 +(ailure after commit and to undo the operation in case)-.1 F .609(of abort.)108 +307.2 R .609 +(After the log message is written, the write system calls are issued.)5.609 F +.61(After all requests are issued,)5.61 F .518(the application calls)108 319.2 +R F1(txn_commit)3.017 E F0 5.517(.W).68 G(hen)256.84 319.2 Q F1(txn_commit) +3.017 E F0 .517(returns, the caller is guaranteed that all necessary log)3.017 +F(writes ha)108 331.2 Q .3 -.15(ve b)-.2 H(een written to disk.).15 E 1.081 +(At an)108 348 R 3.581(yt)-.15 G 1.081(ime, the application may call)142.232 +348 R F1(txn_abort)3.581 E F0 3.581(,w).68 G 1.081 +(hich will result in the appropriate calls to the)318.828 348 R F1 -.37(re) +3.582 G(co).37 E(ver)-.1 E F0 .278(routine to restore the `)108 360 R +(`database')-.74 E 2.778('t)-.74 G 2.778(oac)246.48 360 S .278 +(onsistent pre-transaction state.)265.916 360 R .277(\(The reco)5.277 F -.15 +(ve)-.15 G 2.777(rr).15 G .277(outine must be able to)450.562 360 R +(either reapply or undo the update depending on the conte)108 372 Q +(xt, for each dif)-.15 E(ferent type of log record.\))-.25 E .746 +(If the application should crash, the reco)108 388.8 R -.15(ve)-.15 G .746 +(ry process uses the).15 F F1(db_lo)3.246 E(g)-.1 E F0(interf)3.246 E .746 +(ace to read the log and call the)-.1 F F1 -.37(re)108 400.8 S(co).37 E(ver)-.1 +E F0(routine to restore the database to a consistent state.)2.5 E(The)108 417.6 +Q F1(txn_pr)3.098 E(epar)-.37 E(e)-.37 E F0 .598(function pro)3.098 F .598 +(vides the core functionality to implement distrib)-.15 F .597 +(uted transactions, b)-.2 F .597(ut it does)-.2 F .36 +(not actually manage the noti\214cation of distrib)108 429.6 R .36 +(uted transaction managers.)-.2 F .36(The caller is responsible for issu-)5.36 +F(ing)108 441.6 Q F1(txn_pr)2.82 E(epar)-.37 E(e)-.37 E F0 .32 +(calls to all sites participating in the transaction.)2.82 F .319 +(If all responses are positi)5.319 F -.15(ve)-.25 G 2.819(,t).15 G .319 +(he caller can)488.832 441.6 R .822(issue a)108 453.6 R F1(txn_commit)3.322 E +F0 5.822(.I).68 G 3.322(fa)198.076 453.6 S 1.122 -.15(ny o)209.168 453.6 T +3.322(ft).15 G .822(he responses are ne)236.772 453.6 R -.05(ga)-.15 G(ti).05 E +-.15(ve)-.25 G 3.322(,t).15 G .823(he caller should issue a)349.15 453.6 R F1 +(txn_abort)3.323 E F0 5.823(.I).68 G 3.323(ng)499.747 453.6 S(eneral,)513.07 +453.6 Q(the)108 465.6 Q F1(txn_pr)2.5 E(epar)-.37 E(e)-.37 E F0 +(call requires that the transaction log be \215ushed to disk.)2.5 E .821 +(The structure of the transaction support allo)108 482.4 R .821 +(ws application designers to trade of)-.25 F 3.32(fp)-.25 G .82 +(erformance and protec-)445.07 482.4 R 3.948(tion. Since)108 494.4 R 1.448 +(DB manages man)3.948 F 3.948(ys)-.15 G 1.448(tructures in shared memory)245.36 +494.4 R 3.948(,i)-.65 G 1.448(ts information is subject to corruption by) +367.982 494.4 R 1.306(applications when the library is link)108 506.4 R 1.306 +(ed directly with the application.)-.1 F -.15(Fo)6.306 G 3.805(rt).15 G 1.305 +(his reason, DB is designed to)416.815 506.4 R(allo)108 518.4 Q 3.367(wc)-.25 G +.867(ompilation into a separate serv)137.777 518.4 R .868 +(er process that may be accessed via a sock)-.15 F .868(et interf)-.1 F 3.368 +(ace. In)-.1 F .868(this w)3.368 F(ay)-.1 E(DB')108 530.4 Q 2.828(sd)-.55 G +.328(ata structures are protected from application code, b)136.388 530.4 R .328 +(ut communication o)-.2 F -.15(ve)-.15 G .327(rhead is increased.).15 F(When) +5.327 E(applications are trusted, DB may be compiled directly into the applica\ +tion for increased performance.)108 542.4 Q F2(ERR)72 559.2 Q(ORS)-.27 E F0 +(The)108 571.2 Q F1(txn_cr)4.113 E(eate)-.37 E F0 1.613(function may f)4.113 F +1.613(ail and set)-.1 F F1(errno)4.113 E F0 1.614(for an)4.113 F 4.114(yo)-.15 +G 4.114(ft)349.022 571.2 S 1.614 +(he errors speci\214ed for the library functions)359.246 571.2 R F1(open)108 +583.2 Q F0(\(2\),).24 E F1(write)2.5 E F0(\(2\),).18 E F1(malloc)2.5 E F0 +(\(3\),).31 E F1(loc)2.5 E(k_cr)-.2 E(eate)-.37 E F0(\(3\), and).18 E F1(lo)2.5 +E(g_cr)-.1 E(eate)-.37 E F0(\(3\).).18 E(The)108 600 Q F1(txn_open)2.509 E F0 +.009(function may f)2.509 F .009(ail and set)-.1 F F1(errno)2.508 E F0 .008 +(to an)2.508 F 2.508(yo)-.15 G 2.508(ft)323.916 600 S .008 +(he errors speci\214ed for the library functions)332.534 600 R F1(open)2.508 E +F0(\(2\),).24 E F1(write)108 612 Q F0(\(2\),).18 E F1(malloc)2.5 E F0(\(3\),) +.31 E F1(loc)2.5 E(k_open)-.2 E F0(\(3\), and).24 E F1(lo)2.5 E(g_open)-.1 E F0 +(\(3\).).24 E(The)108 628.8 Q F1(txn_be)2.671 E(gin)-.4 E F0 .171 +(function may f)2.671 F .171(ail and set)-.1 F F1(errno)2.671 E F0 .171 +(to ENOSPC indicating that the maximum number of concur)2.671 F(-)-.2 E +(rent transactions has been reached.)108 640.8 Q(The)108 657.6 Q F1(txn_commit) +2.5 E F0(function may f)2.5 E(ail and set)-.1 E F1(errno)2.5 E F0(to EINV)2.5 E +(AL indicating that the transaction w)-1.35 E(as aborted.)-.1 E(The)108 674.4 Q +F1(txn_close)4.582 E F0 2.082(function may f)4.582 F 2.081(ail and set)-.1 F F1 +(errno)4.581 E F0 2.081(to an)4.581 F 4.581(yo)-.15 G 4.581(ft)345.753 674.4 S +2.081(he errors speci\214ed for the library functions)356.444 674.4 R F1(close) +108 686.4 Q F0(\(2\),).18 E F1 -.37(re)2.5 G(ad).37 E F0(\(2\),).77 E F1(write) +2.5 E F0(\(2\),).18 E F1(fr)2.5 E(ee)-.37 E F0(\(3\),).18 E F1(fsync)2.5 E F0 +(\(2\),).31 E F1(loc)2.5 E(k_close)-.2 E F0(\(3\) or).18 E F1(lo)2.5 E(g_close) +-.1 E F0(\(3\).).18 E(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 +732 Q 99.315(ution August)-.2 F(1, 1995)2.5 E(3)535 732 Q EP +%%Page: 4 28 +%%BeginPageSetup +BP +%%EndPageSetup +/F0 10/Times-Roman@0 SF 124.57(DB_TXN\(3\) BSD)72 48 R(Programmer')2.5 E 2.5 +(sM)-.55 G 124.57(anual DB_TXN\(3\))340.17 48 R(The)108 84 Q/F1 10 +/Times-Italic@0 SF(txn_unlink)4.319 E F0 1.819(function may f)4.319 F 1.819 +(ail and set)-.1 F F1(errno)4.319 E F0 1.819(to an)4.319 F 4.319(yo)-.15 G 4.32 +(ft)347.58 84 S 1.82(he errors speci\214ed for the library functions)358.01 84 +R F1(unlink)108 96 Q F0(\(2\),).67 E F1(loc)2.5 E(k_unlink)-.2 E F0(\(3\), and) +.67 E F1(lo)2.5 E(g_unlink)-.1 E F0(\(3\), or the follo).67 E(wing:)-.25 E([EB) +108 112.8 Q(USY])-.1 E(The transaction re)133 124.8 Q(gion w)-.15 E +(as in use and the force \215ag w)-.1 E(as not set.)-.1 E/F2 9/Times-Bold@0 SF +(SEE ALSO)72 141.6 Q F1(db_btr)108 153.6 Q(ee)-.37 E F0(\(3\),).18 E F1 +(db_hash)2.5 E F0(\(3\),).28 E F1(db_loc)2.5 E(k)-.2 E F0(\(3\),).67 E F1 +(db_lo)2.5 E(g)-.1 E F0(\(3\),).22 E F1(db_mpool)2.5 E F0(\(3\),).51 E F1 +(db_open)2.5 E F0(\(3\),).24 E F1(db_r)2.5 E(ecno)-.37 E F0(\(3\)).18 E F1 .904 +(LIBTP: P)108 177.6 R(ortable)-.8 E 3.404(,M)-.1 G .904(odular T)189.738 177.6 +R -.15(ra)-.55 G .904(nsactions for UNIX).15 F F0 3.404(,M).94 G(ar)328.884 +177.6 Q .904(go Seltzer)-.18 F 3.403(,M)-.4 G .903 +(ichael Olson, USENIX proceedings,)392.041 177.6 R -.4(Wi)108 189.6 S +(nter 1992.).4 E F2 -.09(BU)72 206.4 S(GS).09 E F0(The)108 218.4 Q F1(maxtxns) +2.792 E F0 .292(parameter is a kluge, and should be deleted in f)2.792 F -.2 +(avo)-.1 G 2.793(ro).2 G 2.793(fd)378.448 218.4 S .293(ynamically e)389.571 +218.4 R .293(xpanding the transaction)-.15 F(re)108 230.4 Q(gion.)-.15 E +(4.4 Berk)72 732 Q(ele)-.1 E 2.5(yD)-.15 G(istrib)132.57 732 Q 99.315 +(ution August)-.2 F(1, 1995)2.5 E(4)535 732 Q EP +%%Trailer +end +%%EOF diff --git a/src/util/db2/man/db_btree.3 b/src/util/db2/man/db_btree.3 new file mode 100644 index 000000000..25e289f3c --- /dev/null +++ b/src/util/db2/man/db_btree.3 @@ -0,0 +1,246 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_btree.3 8.11 (Berkeley) 8/1/95 +.\" +.TH DB_BTREE 3 "August 1, 1995" +.UC 7 +.SH NAME +db_btree \- btree database access method +.SH DESCRIPTION +.so db.so +.GN +specific details of the btree access method. +.PP +The btree data structure is a sorted, balanced tree structure storing +associated key/data pairs. +Searches, insertions, and deletions in the btree will all complete in +O lg base N where base is the average fill factor. +Often, inserting ordered data into btrees results in a low fill factor. +This implementation has been modified to make ordered insertion the best +case, resulting in a much better than normal page fill factor. +.SH "ACCESS METHOD SPECIFIC INFORMATION" +The btree access method specific data structure provided to +.I db_open +is typedef'd and named BTREEINFO. +A BTREEINFO structure has at least the following fields, +which may be initialized before calling +.IR db_open : +.TP 5 +u_int cachesize; +A suggested maximum size (in bytes) of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather than fail. +Since every search examines the root page of the tree, caching the most +recently used pages substantially improves access time. +In addition, physical writes are delayed as long as possible, so a moderate +cache can reduce the number of I/O operations significantly. +Obviously, using a cache increases (but only increases) the likelihood of +corruption or lost data if the system crashes while a tree is being modified. +If +.I cachesize +is 0 (no size is specified) a default cache is used. +.TP 5 +int (*compare)(const DBT *, const DBT *); +Compare is the key comparison function. +It must return an integer less than, equal to, or greater than zero if the +first key argument is considered to be respectively less than, equal to, +or greater than the second key argument. +The same comparison function must be used on a given tree every time it +is opened. +If +.I compare +is NULL (no comparison function is specified), the keys are compared +lexically, with shorter keys considered less than longer keys. +.TP 5 +u_long flags; +The flag value is specified by +.IR or 'ing +any of the following values: +.RS +.TP 5 +R_DUP +Permit duplicate keys in the tree, i.e. permit insertion if the key to be +inserted already exists in the tree. +The default behavior, as described in +.IR db_open (3), +is to overwrite a matching key when inserting a new key or to fail if +the R_NOOVERWRITE flag is specified. +The R_DUP flag is overridden by the R_NOOVERWRITE flag, and if the +R_NOOVERWRITE flag is specified, attempts to insert duplicate keys into +the tree will fail. +.IP +If the database contains duplicate keys, the order of retrieval of +key/data pairs is undefined if the +.I get +function is used, however, +.I seq +function calls with the R_CURSOR flag set will always return the logical +``first'' of any group of duplicate keys. +.RE +.TP 5 +int lorder; +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +.TP 5 +int maxkeypage; +The maximum number of keys which will be stored on any single page. +This functionality is not currently implemented. +.\" The maximum number of keys which will be stored on any single page. +.\" Because of the way the btree data structure works, +.\" .I maxkeypage +.\" must always be greater than or equal to 2. +.\" If +.\" .I maxkeypage +.\" is 0 (no maximum number of keys is specified) the page fill factor is +.\" made as large as possible (which is almost invariably what is wanted). +.TP 5 +int minkeypage; +The minimum number of keys which will be stored on any single page. +This value is used to determine which keys will be stored on overflow +pages, i.e. if a key or data item is longer than the pagesize divided +by the minkeypage value, it will be stored on overflow pages instead +of in the page itself. +If +.I minkeypage +is 0 (no minimum number of keys is specified) a value of 2 is used. +.TP 5 +size_t (*prefix)(const DBT *, const DBT *); +Prefix is the prefix comparison function. +If specified, this function must return the number of bytes of the second key +argument which are necessary to determine that it is greater than the first +key argument. +If the keys are equal, the key length should be returned. +Note, the usefulness of this function is very data dependent, but, in some +data sets can produce significantly reduced tree sizes and search times. +If +.I prefix +is NULL (no prefix function is specified), +.B and +no comparison function is specified, a default lexical comparison function +is used. +If +.I prefix +is NULL and a comparison function is specified, no prefix comparison is +done. +.TP 5 +u_int psize; +Page size is the size (in bytes) of the pages used for nodes in the tree. +The minimum page size is 512 bytes and the maximum page size is 64K. +If +.I psize +is 0 (no page size is specified) a page size is chosen based on the +underlying file system I/O block size. +.PP +If the file already exists (and the O_TRUNC flag is not specified), the +values specified for the parameters flags, lorder and psize are ignored +in favor of the values used when the tree was created. +.SH "DB OPERATIONS" +The functions returned by +.I db_open +for the btree access method are as described in +.IR db_open (3), +with the following exceptions and additions: +.TP 5 +type +The type is DB_BTREE. +.TP 5 +del +Space freed up by deleting key/data pairs from the tree is never reclaimed, +although it is reused where possible. +This means that the btree storage structure is grow-only. +The only solutions are to avoid excessive deletions, or to create a fresh +tree periodically from a scan of an existing one. +.TP 5 +put +The +.I put +function takes the following additional flags: +.RS +.TP 5 +R_SETCURSOR +Store the key/data pair, setting or initializing the position of the +cursor to reference it. +.RE +.TP 5 +seq +Forward sequential scans of a tree are from the least key to the greatest. +.IP +The returned key for the +.I seq +function is not necessarily an exact match for the specified key in +the btree access method. +The returned key is the smallest key greater than or equal to the +specified key, permitting partial key matches and range searches. +.IP +The +.I seq +function takes the following additional flags: +.RS +.TP 5 +R_LAST +The last key/data pair of the database is returned, and the cursor +is set or initialized to reference it. +.TP 5 +R_PREV +Retrieve the key/data pair immediately before the cursor. +If the cursor is not yet set, this is the same as the R_LAST flag. +.RE +.SH ERRORS +The +.I btree +access method functions may fail and set +.I errno +for any of the errors specified for the library function +.IR db_open (3). +.SH "SEE ALSO" +.IR db_hash (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_recno (3), +.IR db_txn (3) +.sp +.IR "The Ubiquitous B-tree" , +Douglas Comer, ACM Comput. Surv. 11, 2 (June 1979), 121-138. +.sp +.IR "Prefix B-trees" , +Bayer and Unterauer, ACM Transactions on Database Systems, Vol. 2, 1 +(March 1977), 11-26. +.sp +.IR "The Art of Computer Programming Vol. 3: Sorting and Searching" , +D.E. Knuth, 1968, pp 471-480. diff --git a/src/util/db2/man/db_hash.3 b/src/util/db2/man/db_hash.3 new file mode 100644 index 000000000..adb88fca7 --- /dev/null +++ b/src/util/db2/man/db_hash.3 @@ -0,0 +1,138 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_hash.3 8.13 (Berkeley) 8/1/95 +.\" +.TH DB_HASH 3 "August 1, 1995" +.UC 7 +.SH NAME +db_hash \- hash database access method +.SH DESCRIPTION +.so db.so +.GN +specific details of the hashing access method. +.PP +The hash data structure is an extensible, dynamic hashing scheme. +Backward compatible interfaces to the functions described in +.IR dbm (3), +and +.IR ndbm (3) +are provided, however these interfaces are not compatible with +previous file formats. +.SH "ACCESS METHOD SPECIFIC INFORMATION" +The hash access method specific data structure provided to +.I db_open +is typedef'd and named HASHINFO. +A HASHINFO structure has at least the following fields, +which may be initialized before calling +.IR db_open : +.TP 5 +u_int bsize; +.I Bsize +defines the hash table bucket size, and is, by default, 256 bytes. +It may be preferable to increase the page size for disk-resident tables +and tables with large data items. +.TP 5 +u_int cachesize; +A suggested maximum size, in bytes, of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather +than fail. +.TP 5 +u_int ffactor; +.I Ffactor +indicates a desired density within the hash table. +It is an approximation of the number of keys allowed to accumulate in any +one bucket, determining when the hash table grows or shrinks. +The default value is 8. +.TP 5 +u_int32_t (*hash)(const void *, size_t); +.I Hash +is a user defined hash function. +Since no hash function performs equally well on all possible data, the +user may find that the built-in hash function does poorly on a particular +data set. +User specified hash functions must take two arguments (a pointer to a byte +string and a length) and return a 32-bit quantity to be used as the hash +value. +.IP +If a hash function is specified, +.I hash_open +will attempt to determine if the hash function specified is the same as +the one with which the database was created, and will fail if it is not. +.TP 5 +int lorder; +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +If the file already exists, the specified value is ignored and the +value specified when the tree was created is used. +.TP 5 +u_int nelem; +.I Nelem +is an estimate of the final size of the hash table. +If not set or set too low, hash tables will expand gracefully as keys +are entered, although a slight performance degradation may be noticed. +The default value is 1. +.PP +If the file already exists (and the O_TRUNC flag is not specified), the +values specified for the parameters bsize, ffactor, lorder and nelem are +ignored and the values specified when the tree was created are used. +.SH "DB OPERATIONS" +The functions returned by +.I db_open +for the hash access method are as described in +.IR db_open (3). +.SH ERRORS +The +.I hash +access method functions may fail and set +.I errno +for any of the errors specified for the library function +.IR db_open (3). +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_recno (3), +.IR db_txn (3) +.sp +.IR "Dynamic Hash Tables" , +Per-Ake Larson, Communications of the ACM, April 1988. +.sp +.IR "A New Hash Package for UNIX" , +Margo Seltzer, USENIX Proceedings, Winter 1991. diff --git a/src/util/db2/man/db_lock.3 b/src/util/db2/man/db_lock.3 new file mode 100644 index 000000000..b18a38c60 --- /dev/null +++ b/src/util/db2/man/db_lock.3 @@ -0,0 +1,462 @@ +.\" Copyright (c) 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_lock.3 8.14 (Berkeley) 8/1/95 +.\" +.TH DB_LOCK 3 "August 1, 1995" +.UC 7 +.SH NAME +db_lock \- general purpose lock manager +.SH SYNOPSIS +.nf +.ft B +#include + +int +lock_create(const char *path, mode_t mode, +.ti +5 +int lock_modes, const int8_t conflicts[][], u_int maxlocks); + +LOCK_TABLE_T * +lock_open(const char *path); + +int +lock_vec(LOCK_TABLE_T *lt, DBT *locker, struct timespec *timeout, +.ti +5 +LOCK_REQ_T list[], int nlist, LOCK_REQ_T **elistp, DBT *conflict); + +int +lock_get(LOCK_TABLE_T *lt, const DBT *locker, +.ti +5 +const DBT *obj, const lock_mode_t lock_mode, LOCK_T **lockp); + +int +lock_put(LOCK_T *lockp); + +int +lock_close(LOCK_TABLE_T *lt); + +int +lock_unlink(const char *path, int force); +.ft R +.fi +.SH DESCRIPTION +.so db.so +.GN +specific details of the locking interface. +.PP +.I Db_lock +is the library interface intended to provide general-purpose locking. +While designed to work with the other DB functions, these functions are +also useful for more general locking purposes. +Locks can be shared between processes. +.PP +.CR "lock table" lock +.PP +The parameter +.I lock_modes +is the number of lock modes to be recognized by the lock table +(including the ``not-granted'' mode). +The parameter +.I conflicts +is an +.I lock_modes +by +.I lock_modes +array. +A non-0 value for: +.sp +.ti +5 +conflicts[requested_mode][held_mode] +.sp +indicates that +.I requested_mode +and +.I held_mode +conflict. +The ``not-granted'' mode must be represented by 0. +.PP +The include file declares two commonly used conflict arrays: +.TP 5 +int lock_sx_n; +.br +.ns +.TP 5 +const int8_t lock_sx_c[lock_sx_n][lock_sx_n]; +These variables specify a conflict array +for a simple scheme using shared and exclusive lock modes. +.TP 5 +int lock_g_n; +.br +.ns +.TP 5 +const int8_t lock_g_c[lock_g_n][lock_g_n]; +These variables specify a conflict array that involves various intent +lock modes (e.g. intent shared) that are used for multigranularity locking. +.PP +In addition, + defines the following macros that name lock modes for use with +the standard tables above: +.RS +.TP 5 +LOCK_IS +intent shared +.br +.ns +.TP 5 +LOCK_IX +intent exclusive +.br +.ns +.TP 5 +LOCK_NG +not granted (always 0) +.br +.ns +.TP 5 +LOCK_S +shared +.br +.ns +.TP 5 +LOCK_SIX +shared/intent exclusive +.br +.ns +.TP 5 +LOCK_X +exclusive +.RE +.PP +.I Maxlocks +is the maximum number of locks to be held or requested in the table, +and is used by +.I lock_create +to estimate how much space to allocate for various lock-table data +structures. +.PP +.RT lock_create +.PP +.OP "lock table" lock +.PP +The function +.I lock_vec +atomically obtains and releases one or more locks from the designated +table. +The function +.I lock_vec +is intended to support acquisition or trading of multiple locks +under one lock table semaphore, as is needed for lock coupling or +in multigranularity locking for lock escalation. +.PP +If any of the requested locks cannot be acquired +or any of the locks to be released cannot be released, +no locks are acquired and no locks are released, and +.I lock_vec +returns an error. +The function +.I lock_vec +returns 0 on success. +If an error occurs, +.I lock_vec +returns one of the following values. +In addition, if +.I elistp +is not NULL, it is set to point to the LOCK_REQ_T entry which +was being processed when the error occurred. +.TP 5 +LOCK_GET_DEADLOCK +The specified +.I locker +was selected as a victim in order to resolve a deadlock. +In this case, if the +.I conflict +argument is non-NULL, it is set to reference the identity of a +locker holding the lock referenced by +.I elistp +at the time the request was denied. +(This identity resides in static memory and may be overwritten by +subsequent calls to +.IR lock_vec ). +.TP 5 +LOCK_GET_ERROR +An error occurred and the external variable +.I errno +has been set to indicate the error. +.TP 5 +LOCK_GET_NOTHELD +The lock cannot be released, as it was not held by the +.IR locker . +.TP 5 +LOCK_GET_RESOURCE +The lock manager is unable to grant the requested locks because of +limited internal resources. +(Releasing locks may allow future calls to +.I lock_vec +to succeed.) +.TP 5 +LOCK_GET_TIMEOUT +A timeout argument was specified, and the requested locks were not +available soon enough. +In this case, if the +.I conflict +argument is non-NULL, it is set to reference the identity of a +locker holding the lock referenced by +.I elistp +at the time the request was denied. +(This identity resides in static memory and may be overwritten by +subsequent calls to +.IR lock_vec ). +.PP +The +.I locker +argument specified to +.I lock_vec +is a pointer to an untyped byte string which identifies the entity +requesting or releasing the lock. +If +.I locker +is NULL, the calling process' pid is used instead. +.PP +The +.I timeout +argument provided to +.I lock_vec +specifies a maximum interval to wait for the locks to be granted. +If +.I timeout +is NULL, it is ignored, and +.I lock_vec +will not return until all of the locks are acquired or an error has +occurred. +.PP +The +.I list +array provided to +.I lock_vec +is typedef'd in as LOCK_REQ_T. +A LOCK_REQ_T structure has at least the following fields, +which must be initialized before calling +.IR lock_vec : +.TP 5 +enum lockop op; +The operation to be performed, which must be set to one of the +following values: +.RS +.TP 5 +LOCK_GET +Get a lock, as defined by the values of +.IR locker , +.I obj +and +.IR lock_mode . +Upon return from +.IR lock_vec , +if the +.I lockp +field is non-NULL, a reference to the acquired lock is stored there. +(This reference is invalidated by any call to +.I lock_vec +or +.I lock_put +which releases the lock.) +.TP 5 +LOCK_PUT +The lock referenced by the contents of the +.I lockp +field is released. +.TP 5 +LOCK_PUT_ALL +All locks held by the +.I locker +are released. +(Any locks acquired as a part of the current call to +.I lock_vec +are not considered for this operation). +.TP 5 +LOCK_PUT_OBJ +All locks held by the +.IR locker , +on the object +.IR obj , +with the mode specified by +.IR lock_mode , +are released. +A +.I lock_mode +of LOCK_NG indicates that all locks on the object should be released. +(Any locks acquired as a part of the current call to +.I lock_vec +are not considered for this operation). +.RE +.TP 5 +const DBT obj; +An untyped byte string which specifies the object to be locked or +released. +.TP 5 +const lock_mode_t lock_mode; +The lock mode, used as an index into +.IR lt 's +conflict array. +.TP 5 +LOCK_T **lockp; +A pointer to a pointer to a lock reference. +.PP +The +.I nlist +argument specifies the number of elements in the +.I list +array. +.PP +The function +.I lock_get +is a simple interface to the +.I lock_vec +functionality, and is equivalent to calling the +.I lock_vec +function with the +.I lt +and +.I locker +arguments, NULL +.IR timeout , +.I elistp +and +.I conflict +arguments, and a single element +.I list +array, for which the +.I op +field is LOCK_GET, and the +.IR obj , +.I lock_mode +and +.I lockp +fields are represented by the arguments of the same name. +Note that the type of the +.I obj +argument to +.I lock_get +is different from the +.I obj +element found in the LOCK_REQ_T structure. +The +.I lock_get +function returns success and failure as described for the +.I lock_vec +function. +.PP +The function +.I lock_put +is a simple interface to the +.I lock_vec +functionality, and is equivalent to calling the +.I lock_vec +function with a single element +.I list +array, for which the +.I op +field is LOCK_PUT and the +.I lockp +field is represented by the argument of the same name. +Note that the type of the +.I lockp +argument to +.I lock_put +is different from the +.I lockp +element found in the LOCK_REQ_T structure. +The +.I lock_put +function returns success and failure as described for the +.I lock_vec +function. +.PP +The function +.I lock_close +disassociates the calling process from the lock table +.IR lt , +after releasing all locks held or requested by that process. +.RT lock_close +.PP +.UN "lock table" lock +.SH "ERRORS" +The +.I lock_create +function may fail and set +.I errno +for any of the errors specified for the library routines +.IR mmap (2), +.IR open (2) +and +.IR malloc (3). +.PP +The +.I lock_open +function may fail and set +.I errno +for any of the errors specified for the library routine +.IR mmap (2) +and +.IR open (2). +.PP +The +.I lock_close +function may fail and set +.I errno +for any of the errors specified for the library routine +.IR close (2) +and +.IR munmap (2). +.PP +The +.I lock_unlink +function may fail and set +.I errno +for any of the errors specified for the library function +.IR unlink (2) +or the following: +.TP 5 +[EBUSY] +The lock table was in use and the force flag was not set. +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_recno (3), +.IR db_txn (3) +.SH BUGS +The +.I maxlocks +parameter is a kluge, and should be deleted in favor of dynamically +expanding the lock table. diff --git a/src/util/db2/man/db_log.3 b/src/util/db2/man/db_log.3 new file mode 100644 index 000000000..34c4d1f5d --- /dev/null +++ b/src/util/db2/man/db_log.3 @@ -0,0 +1,290 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_log.3 8.15 (Berkeley) 8/3/95 +.\" +.TH DB_LOG 3 "August 3, 1995" +.UC 7 +.SH NAME +db_log \- log-manager access method +.SH DESCRIPTION +.so db.so +.GN +specific details of the logging access method. +.PP +These functions provide a general-purpose logging facility sufficient +for transaction management. +Logs can be shared by multiple processes. +.PP +A log is represented by the directory, +.IR "not the file" , +named by the first argument to +.IR db_open (3). +The first argument must be non-NULL, +and the directory must already exist +.I db_open +is called. +In that directory, the log is stored in one or more files named +in the format ``log.YYYY.MM.DD.HH.MM.SS'', where ``YYYY.MM.DD.HH.SS'' +is the approximate creation time of the log file, and is guaranteed +to be unique in the directory. +.PP +The group of the created files is based on the system and directory +defaults, and is not further specified by the log access method. +All files are created with the +.I mode +specified to +.IR db_open , +(as described in +.IR chmod (2)) +and modified by the process' umask value (see +.IR umask (2)). +.PP +The +.I flags +argument to +.I db_open +must be 0 for the +.I db_log +access method. +.SH "ACCESS METHOD SPECIFIC INFORMATION" +The log access method specific data structure provided to +.I db_open +is typedef'd and named LOGINFO. +A LOGINFO structure has at least the following fields, +which may be initialized before calling +.IR db_open : +.TP 5 +off_t max_file_size; +The maximum size of a single file in the log. +If not specified, the maximum size defaults to an implementation-specific +value. +.TP 5 +int lorder; +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +.PP +If the log already exists, the values specified for the parameters +max_file_size and lorder are ignored in favor of the values used +when the log was created. +.SH "DB OPERATIONS" +The data part of the key/data pair used by the log access method +is the same as for other access methods. +The key is different. +Each log record is identified by a log sequence number (LSN), +which is stored in a DBT, and which is used as the +.I key +for all log functions that take +.I key +arguments. +Applications cannot create LSN's, and all LSN's provided to functions +as arguments must first be retrieved using the +.I put +or +.I seq +functions. +To provide a distinguished value for applications, it is guaranteed that +no valid LSN will ever have a size of 0. +.PP +Applications can compare LSN's using the +.I log_lsn_compare +function (see below). +.PP +Applications can associate LSN's with specific log files. +The function +.I log_lsn_file +(see below), returns the name of the log file containing the +record with a specified LSN. +(The mapping of LSN to file is needed for database administration. +For example, a transaction manager typically records the earliest LSN +needed for restart, and the database administrator may want to archive +log files to tape when they contain only LSN's before the earliest one +needed for restart.) +.PP +Applications can truncate the log file up to a specific LSN using the +.I log_trunc +function (see below). +.PP +The functions returned by +.I db_open +for the log access method are as described in +.IR db_open , +with the following exceptions and additions: +.TP 5 +type +The type is DB_LOG. +.TP 5 +del +The +.I del +function always returns an error for the log-manager access method, +setting +.I errno +to EINVAL. +.TP 5 +int (*log_flush)(const DB *db, const DBT *lsn); +The +.I log_flush +function flushes the log up to and including the log record +.IR lsn . +.RT log_flush +.TP 5 +int (*log_lsn_compare)(const DB *, +.ti +5 +const DBT *lsn1, const DBT *lsn2); +A pointer to a function which is provided to permit applications to +compare LSN's. +The +.I log_lsn_compare +function returns an integer less than, equal to, or greater than zero +if the first LSN is considered to be respectively less than, equal to, +or greater than the second LSN. +.TP 5 +int (*log_lsn_file)(const DB *db, +.ti +5 +const DBT *lsn, char *name); +.br +The +.I log_lsn_file +function stores a pointer to the name of the file containing +.I lsn +in the address referenced by +.IR name. +This pointer is to an internal static object, and subsequent calls to +the same function will modify the same object. +.IP +.RT log_lsn_file +.TP 5 +int (*log_unlink)(const char *path, int force); +The +.I log_unlink +function destroys the log represented by +.IR path . +If the +.I force +parameter is not set to 1 and there are other processes using the +log, then +.I log_unlink +will return -1, setting +.IR errno +to EBUSY. +If +.I force is not set or there are no processes using the log, then all files +used by the log are destroyed. +.I log_unlink +will return -1 on failure, setting +.IR errno , +and 0 on success. +.TP 5 +int (*log_trunc)(const DB *db, const DBT *lsn); +The +.I log_trunc +function truncates the log up to an LSN which is less than +.IR lsn . +.RT log_trunc +.TP 5 +put +A log record containing +.I data +is appended to the log. +Unlike the +.I put +functions for other access methods, the key parameter is not initialized +by the application, instead, the LSN assigned to the data is returned in +the +.I key +parameter. +.IP +The caller is responsible for providing any necessary structure to +.I data . +(For example, in a write-ahead logging protocol, the application must +understand what part of +.I data +is an operation code, what part is redo information, and what part is +undo information. +In addition, most transaction managers will store in +.I data +the LSN of the previous log record for the same transaction, +to support chaining back through the transaction's log records +during undo.) +.IP +The parameter +.I flag +must be set to 0 or exactly one of the following values: +.RS +.TP 5 +R_CHECKPOINT +Specify the key/data pair of the current call as the one to be returned +when the +.I seq +function is next called with the R_CHECKPOINT flag. +.TP 5 +R_FLUSH +Flush immediately (ignoring any possibility for group commit). +.RE +.TP 5 +seq +The +.I seq +function takes the following additional flag: +.RS +.TP 5 +R_CHECKPOINT +The last key/data pair stored by the +.I put +function (using the R_CHECKPOINT flag) is returned, +and the cursor is set or initialized to reference it. +The expected use of this flag is during restart and to determine what +part of the log must be available for restart. +Therefore, the log record retrieved with R_CHECKPOINT should contain +all the information that the transaction manager will need for this +purpose. +.RE +.TP 5 +sync +The +.I sync +function always returns an error for the log-manager access method, +setting +.I errno +to EINVAL. +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_lock (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_recno (3), +.IR db_txn (3) diff --git a/src/util/db2/man/db_mpool.3 b/src/util/db2/man/db_mpool.3 new file mode 100644 index 000000000..4b683b618 --- /dev/null +++ b/src/util/db2/man/db_mpool.3 @@ -0,0 +1,403 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_mpool.3 8.14 (Berkeley) 8/1/95 +.\" +.TH DB_MPOOL 3 "August 1, 1995" +.UC 7 +.SH NAME +db_mpool \- general purpose shared memory buffer pool +.SH SYNOPSIS +.nf +.ft B +#include +#include + +int +mpool_create(char *path, mode_t mode, size_t cachesize, u_long flags); + +MPOOL * +mpool_open(char *path); + +int +mpool_close(MPOOL *mp); + +MPOOLFILE * +mpool_fopen(MPOOL *mp, char *path, size_t pagesize, void *pgcookie, +.ti +5 +int (*pgin)(MPOOLFILE *mpf, +.ti +8 +pgno_t pgno, void *pgaddr, void *pgcookie), +.ti +5 +int (*pgout)(MPOOLFILE *mpf, +.ti +8 +pgno_t pgno, void *pgaddr, void *pgcookie); + +int +mpool_fclose(MPOOLFILE *mpf); + +void * +mpool_get(MPOOLFILE *mpf, pgno_t *pgnoaddr, u_long flags, +.ti +5 +int (*callback)(MPOOLFILE *mpf, pgno_t pgno)); + +int +mpool_put(MPOOLFILE *mpf, void *pgaddr, u_long flags); + +int +mpool_sync(MPOOLFILE *mpf); + +int +mpool_unlink(const char *path, int force); + +void +mpool_stat(MPOOL *mp, FILE *fp); +.ft R +.fi +.SH DESCRIPTION +.so db.so +.GN +specific details of the memory pool interface. +.PP +The +.I db_mpool +function is the library interface intended to provide general-purpose, +page-oriented buffer management of one or more files. +While designed to work with the other DB functions, these functions are +also useful for more general purposes. +The memory pools (MPOOL's) are referred to in this document as +simply ``pools''. +Pools may be shared between processes. +Pools are usually filled by pages from one or more files (MPOOLFILE's). +Pages in the pool are replaced in LRU (least-recently-used) order, +with each new page replacing the page which has been unused the longest. +Pages retrieved from the pool using +.I mpool_get +are ``pinned'' in memory, by default, +until they are returned to the pool using the +.I mpool_put +function. +.PP +.CR "memory pool" mpool +.PP +The +.I cachesize +argument specifies the size of the pool in bytes, +and should be the size of the normal working set of the application with +some small amount of additional memory for unusual situations. +If the number of bytes currently ``pinned'' in memory exceeds +.IR cachesize , +the +.I db_mpool +functions will attempt to allocate more memory and do not necessarily fail, +although they may suffer performance degradation. +.PP +The +.I flags +argument is set by +.IR or 'ing +any of the following values: +.TP +MPOOL_PRIVATE +The pool is not shared by other processes or threads, +so no locking of pool resources is required. +.PP +.OP "memory pool" mpool +.PP +The +.I mpool_close +function closes the pool indicated by the MPOOL pointer +.IR mp , +as returned by +.IR mpool_open . +This function does +.B not +imply a call to +.I mpool_sync +(or to +.IR mpool_fclose ) +i.e. no pages are written to the source file as as a result of calling +.IR mpool_close . +.RT mpool_close +.PP +The function +.I mpool_fopen +opens a file for buffering in the pool specified by the MPOOL +argument. +The +.I path +argument is the name of the file to be opened. +The +.I pagesize +argument is the size, in bytes, of the unit of transfer between the +application and the pool, although not necessarily the unit of transfer +between the pool and the source file. +Applications not knowing the page size of the source file should +retrieve the metadata from the file using a page size that is correct +for the metadata, then close and reopen the file, or, +otherwise determine the page size before calling +.IR mpool_fopen . +.PP +If the +.I pgin +function is specified, it is called each time a page is read into +the memory pool from the source file. +If the +.I pgout +function is specified, it is called each time a page is written +to the source file. +Both functions are called with the MPOOLFILE pointer returned from +.IR mpool_fopen , +the page number, a pointer to the page being read or written, and +the argument +.IR pgcookie . +If either function fails, it should return non-zero and set +.IR errno , +in which case the +.I db_mpool +function calling it will also fail, leaving +.I errno +intact. +.PP +The +.I mpool_fclose +function closes the source file indicated by the MPOOLFILE pointer +.IR mpf . +This function does +.B not +imply a call to +.IR mpool_sync , +i.e. no pages are written to the source file as as a result of calling +.IR mpool_fclose . +.RT mpool_fclose +.\" +.\".PP +.\"int +.\"mpool_fd (MPOOLFILE *mpf); +.\" +.\".PP +.\"The function +.\".I mpool_fd +.\"takes an MPOOLFILE pointer and returns the file descriptor being +.\"used to read/write that file +.\"to/from the pool. +.PP +The function +.I mpool_get +returns a pointer to the page with the page number specified by +.IR pgnoaddr , +from the source file specified by the MPOOLFILE pointer +.IR mpf . +If the page does not exist or cannot be retrieved, +.I mpool_get +returns NULL and sets errno. +.PP +The +.I flags +argument is set by +.IR or 'ing +any of the following values: +.TP 5 +MPOOL_CALLBACK +After the page number has been determined, but before any other +process or thread can access the page, the function specified by +the +.I callback +argument is called. +If the function fails, it should return non-zero and set +.IR errno , +in which case +.I mpool_get +will also fail, leaving +.I errno +intact. +The +.I callback +function is called with the MPOOLFILE pointer returned from +.I mpool_fopen +and the page number. +This functionality is commonly used when page locking is required, +but the page number of the page being retrieved is not known. +.TP 5 +MPOOL_CREATE +If the specified page does not exist, create it. +.TP 5 +MPOOL_LAST +Return the last page of the source file and copy its page number +to the location referenced by +.IR pgnoaddr . +.TP 5 +MPOOL_NEW +Create a new page in the file and copy its page number to the location +referenced by +.IR pgnoaddr . +.TP 5 +MPOOL_NOPIN +Don't pin the page into memory. +(This flag is intended for debugging purposes, when it's often useful +to examine pages which are currently held by other parts of the +application. +Pages retrieved in this manner don't need to be returned to the +memory pool, i.e. they should +.B not +be specified as arguments to the +.IR mpool_put +routine.) +.PP +Created pages have all their bytes set to 0. +.PP +All pages returned by +.I mpool_get +(unless the MPOOL_NOPIN flag is specified), +will be retained (i.e. ``pinned'') in the pool until a subsequent +call to +.IR mpool_put . +.PP +The function +.I mpool_put +indicates that the page referenced by +.I pgaddr +can be evicted from the pool. +.I Pgaddr +must be an address previously returned by +.IR mpool_get . +.PP +The flag value is specified by +.IR or 'ing +any of the following values: +.TP 5 +MPOOL_DIRTY +The page has been modified and must be written to the source file +before being evicted from the pool. +.TP 5 +MPOOL_DISCARD +The page is unlikely to be useful in the near future, and should be +discarded before other pages in the pool. +.PP +.RT mpool_put +.PP +The function +.I mpool_sync +writes all pages associated with the MPOOLFILE pointer +.IR mpf , +which were specified as arguments to the +.I mpool_put +function with an associated flag of +MPOOL_DIRTY, +to the source file. +.RT mpool_sync +.PP +.UN "memory pool" mpool +.PP +The function +.I mpool_stat +writes statistics for the memory pool +.I mp +to the file specified by +.IR fp . +These statistics include the number of files participating in the pool, +the active pages in the pool, and numbers as to how effective the cache +has been. +.SH ERRORS +The +.IR mpool_create , +.I mpool_open +and +.I mpool_fopen +functions may fail and set +.I errno +for any of the errors specified for the library functions +.IR open (2), +.IR read (2), +and +.IR malloc (3). +.PP +The +.I mpool_close +and +.I mpool_fclose +functions may fail and set +.I errno +for any of the errors specified for the library functions +.IR close (2) +and +.IR free (3). +.PP +The +.I mpool_get +function may fail and set +.I errno +for any of the errors specified for the library functions +.IR read (2), +.IR write (2), +and +.IR malloc (3) +or the following: +.TP 5 +[EINVAL] +The requested page does not exist and MPOOL_CREATE was not set. +.PP +The +.I mpool_put +function may fail and set +.I errno +for any of the errors specified for the library function +.IR write (2) +or the following: +.TP 5 +[EACCES] +The source file was not opened for writing. +.PP +The +.I mpool_sync +function may fail and set +.I errno +for any of the errors specified for the library function +.IR write (2). +.PP +The +.I mpool_unlink +function may fail and set +.I errno +for any of the errors specified for the library function +.IR unlink (2) +or the following: +.TP 5 +[EBUSY] +The memory pool was in use and the force flag was not set. +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_open (3), +.IR db_recno (3), +.IR db_txn (3) diff --git a/src/util/db2/man/db_open.3 b/src/util/db2/man/db_open.3 new file mode 100644 index 000000000..f988ef924 --- /dev/null +++ b/src/util/db2/man/db_open.3 @@ -0,0 +1,574 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_open.3 8.15 (Berkeley) 8/1/95 +.\" +.TH DB_OPEN 3 "August 1, 1995" +.UC 7 +.SH NAME +db_open \- database access methods +.SH SYNOPSIS +.nf +.ft B +#include + +DB * +db_open(const char *file, int flags, int mode, +.ti +5 +DBTYPE type, DBINFO *dbinfo, const void *openinfo); +.ft R +.fi +.SH DESCRIPTION +.so db.so +.GN +the overall structure of the available access methods. +.PP +The currently supported file formats are btree, hashed, log and recno +(i.e. flat-file oriented). +The btree format is a representation of a sorted, balanced tree structure. +The hashed format is an extensible, dynamic hashing scheme. +The log format is a general-purpose logging facility. +The recno format is a byte stream file with fixed or variable length +records. +The formats and other, format specific information are described in detail +in their respective manual pages: +.IR db_btree (3), +.IR db_hash (3), +.IR db_log (3), +and +.IR db_recno (3). +.PP +Db_open opens +.I file +for reading and/or writing. +Files never intended to be preserved on disk may be created by setting +the file parameter to NULL. +(Note, while most of the access methods use +.I file +as the name of an underlying file on disk, this is not guaranteed. +See the manual pages for the individual access methods for more +information.) +.PP +The +.I flags +and +.I mode arguments +are as specified to the +.IR open (2) +function, however, only the O_CREAT, O_EXCL, O_EXLOCK, O_NONBLOCK, +O_RDONLY, O_RDWR, O_SHLOCK and O_TRUNC flags are meaningful. +(Note, opening a database file O_WRONLY is not possible.) +.\"Three additional options may be specified by +.\".IR or 'ing +.\"them into the +.\".I flags +.\"argument. +.\".TP +.\"DB_LOCK +.\"Do the necessary locking in the database to support concurrent access. +.\"If concurrent access isn't needed or the database is read-only this +.\"flag should not be set, as it tends to have an associated performance +.\"penalty. +.\".TP +.\"DB_SHMEM +.\"Place the underlying memory pool used by the database in shared +.\"memory. +.\"Necessary for concurrent access. +.\".TP +.\"DB_TXN +.\"Support transactions in the database. +.\"The DB_LOCK and DB_SHMEM flags must be set as well. +.PP +The +.I type +argument is of type DBTYPE (as defined in the include file) and +may be set to DB_BTREE, DB_HASH, DB_LOG or DB_RECNO. +.PP +The +.I dbinfo +argument is a pointer to a structure containing references to locking, +logging, transaction, and shared-memory buffer pool information. +If +.I dbinfo +is NULL, then the access method may still use these subsystems, +but the usage will be private to the application and managed by DB. +If +.I dbinfo +is non-NULL, +then the module referenced by each of the non-NULL fields is used by DB +as necessary. +The fields of the DBINFO structure are defined as follows: +.TP 5 +const char *errpfx; +A prefix to prepend to error messages; used only if +.I errfile +is non-NULL. +.TP 5 +FILE *errfile; +The +.IR stdio (3) +file stream to which error messages are logged. +.sp +When any error occurs in the +.I db_open +function, or in any function called using a field of the returned DB +structure, an error value is returned by the function, +and the global variable +.I errno +is set appropriately. +In some cases, however, the +.I errno +value may be insufficient to describe the cause of the error. +In these cases, if +.I errfile +is non-NULL, additional error information will be written to the file +stream it represents, preceded by the string, if any, specified by +.IR errpfx . +This error logging facility should not be required for normal operation, +but may be useful in debugging applications. +.TP 5 +char *errbuf; +The buffer to which error messages are copied. +If non-NULL, +.I errbuf +behaves as described for +.IR errfile , +except that the +.I errpfx +field is ignored and the error message is copied into the specified +buffer instead of being written to the FILE stream. +The DB routines assume that the associated buffer is at least 1024 bytes +in length. +.TP 5 +LOCK_TABLE_T *lockinfo; +If locking is required for the file being opened (as in the case of +buffers being maintained in a shared memory buffer pool), +the +.I lockinfo +field contains a return value from the function +.I lock_open +that should be used +(see +.IR db_lock (3)). +If +.I lockinfo +is NULL, no locking is done. +.TP 5 +DB *loginfo; +If modifications to the file being opened should be logged, the +.I loginfo +field contains a return value from the function +.IR dbopen , +when opening a DB file of type DB_LOG. +If +.I loginfo +is NULL, no logging is done. +.TP 5 +MPOOL *mpoolinfo; +If the cache for the file being opened should be maintained in a shared +buffer pool, the +.I mpoolinfo +field contains a return value from the function +.I mpool_open +that should be used +(see +.IR db_mpool (3)). +If +.I mpoolinfo +is NULL, a memory pool may still be created, +but it will be private to the application and managed by DB. +.TP 5 +TXNMGR *txninfo; +If the accesses to the file being opened should take place in the context +of transactions (providing atomicity and complete error recovery), the +.I txninfo +field contains a return value from the function +.I txn_open +(see +.IR db_txn (3)). +If transactions are specified, +the application is responsible for making suitable calls to +.IR txn_begin , +.IR txn_abort , +and +.IR txn_commit . +If +.I txninfo +is NULL, no transaction support is done. +.PP +The +.I openinfo +argument is a pointer to an access method specific structure described +in the access method's manual page. +If +.I openinfo +is NULL, each access method will use defaults appropriate for the system +and the access method. +.SH "KEY/DATA PAIRS" +Access to all access methods is based on key/data pairs. +Both keys and data are represented by the following data structure: +.PP +typedef struct { +.RS +void *data; +.br +size_t size; +.RE +} DBT; +.PP +The elements of the DBT structure are defined as follows: +.TP 5 +data +A pointer to a byte string. +.ns +.br +.TP 5 +size +The length of +.IR data , +in bytes. +.PP +Key and data byte strings may reference strings of essentially unlimited +length, although any two of them must fit into available memory at the +same time. +.PP +The access methods provide no guarantees about byte string alignment, +and applications are responsible for maintaining any necessary alignment. +.SH "DB OPERATIONS" +.I Db_open +returns a pointer to a DB structure (as defined in the include file) +on success, and NULL on error. +The DB structure describes a database type, and includes a set of functions +to perform various actions, as described below. +Each of these functions takes a pointer to a DB structure, and may take +one or more DBT *'s and a flag value as well. +Individual access methods specify additional functions and flags which +are specific to the method. +The fields of the DB structure are as follows: +.TP 5 +DBTYPE type; +The type of the underlying access method (and file format). +.TP 5 +int (*close)(const DB *db); +A pointer to a function to flush any cached information to disk, +free any allocated resources, and close any underlying files. +Since key/data pairs are cached in memory, failing to sync the +file with the +.I close +or +.I sync +function may result in inconsistent or lost information. +.IP +The +.I close +functions return -1 on failure, setting +.IR errno , +and 0 on success. +.TP 5 +int (*del)(const DB *db, TXN *txnid, +.ti +5 +const DBT *key, u_int flags); +.br +A pointer to a function to remove key/data pairs from the database. +The key/data pair associated with the specified +.I key +are discarded from the database. +.IP +The +.I txnid +parameter contains a transaction ID returned from +.IR txn_begin , +if the file is being accessed under transaction protection, +or NULL if transactions are not in effect. +.IP +The parameter +.I flag +must be set to 0 or exactly one of the following values: +.RS +.TP 5 +R_CURSOR +Delete the record referenced by the cursor. +The cursor must have previously been initialized. +.RE +.IP +The +.I delete +functions return -1 on error, setting +.IR errno , +0 on success, and 1 if the specified +.I key +did not exist in the file. +.TP 5 +int (*fd)(const DB *db); +A pointer to a function which returns a file descriptor representative +of the underlying database. +A file descriptor referencing the same file will be returned to all +processes which call +.I db_open +with the same +.I file +name. +This file descriptor may be safely used as an argument to the +.IR fcntl (2) +and +.IR flock (2) +locking functions. +The file descriptor is not necessarily associated with any of the +underlying files used by the access method. +No file descriptor is available for in memory databases. +.IP +The +.I fd +functions return -1 on error, setting +.IR errno , +and the file descriptor on success. +.TP 5 +int (*get)(const DB *db, TXN *txnid, +.ti +5 +const DBT *key, DBT *data, u_int flags); +.br +A pointer to a function which is the interface for keyed retrieval from +the database. +The address and length of the data associated with the specified +.I key +are returned in the structure referenced by +.IR data . +.IP +The +.I txnid +parameter contains a transaction ID returned from +.IR txn_begin , +if the file is being accessed under transaction protection, +or NULL if transactions are not in effect. +.IP +The +.I get +functions return -1 on error, setting +.IR errno , +0 on success, and 1 if the +.I key +was not found. +.TP 5 +int (*put)(const DB *db, TXN *txnid, +.ti +5 +DBT *key, const DBT *data, u_int flags); +.br +A pointer to a function to store key/data pairs in the database. +.IP +The +.I txnid +parameter contains a transaction ID returned from +.IR txn_begin , +if the file is being accessed under transaction protection, +or NULL if transactions are not in effect. +.IP +The parameter +.I flag +must be set to 0 or exactly one of the following values: +.RS +.TP 5 +R_CURSOR +Replace the key/data pair referenced by the cursor. +The cursor must have previously been initialized. +.TP 5 +R_NOOVERWRITE +Enter the new key/data pair only if the key does not previously exist. +.RE +.IP +The default behavior of the +.I put +functions is to enter the new key/data pair, replacing any previously +existing key. +.IP +The +.I put +functions return -1 on error, setting +.IR errno , +0 on success, and 1 if the R_NOOVERWRITE +.I flag +was set and the key already exists in the file. +.TP 5 +int (*seq)(const DB *db, TXN *txnid, +.ti +5 +DBT *key, DBT *data, u_int flags); +.br +A pointer to a function which is the interface for sequential +retrieval from the database. +The address and length of the key are returned in the structure +referenced by +.IR key , +and the address and length of the data are returned in the +structure referenced +by +.IR data . +.IP +The +.I txnid +parameter contains a transaction ID returned from +.IR txn_begin , +if the file is being accessed under transaction protection, +or NULL if transactions are not in effect. +.IP +Sequential key/data pair retrieval may begin at any time, and the +logical position of the ``cursor'' is not affected by calls to the +.IR del , +.IR get , +.IR put , +or +.I sync +functions. +Modifications to the database during a sequential scan will be reflected +in the scan, i.e. records inserted behind the cursor will not be returned +while records inserted in front of the cursor will be returned. +.IP +The parameter +.I flag +must be set to 0 or exactly one of the following values: +.RS +.TP 5 +R_CURSOR +The data associated with the specified key is returned. +This differs from the +.I get +functions in that it sets or initializes the cursor to the location of +the key as well. +.TP 5 +R_FIRST +The first key/data pair of the database is returned, and the cursor +is set or initialized to reference it. +.TP 5 +R_NEXT +Retrieve the key/data pair immediately after the cursor. +If the cursor is not yet set, this is the same as the R_FIRST flag. +.RE +.IP +The +.I seq +functions return -1 on error, setting +.IR errno , +0 on success, +and 1 if there are no key/data pairs less than or greater than the +specified or current key. +.TP 5 +int (*sync)(const DB *db, u_int flags); +A pointer to a function to flush any cached information to disk. +If the database is in memory only, the +.I sync +function has no effect and will always succeed. +.IP +The parameter +.I flag +must be set to 0 or a value specified by an access method specific +manual page. +.IP +The +.I sync +functions return -1 on failure, setting +.IR errno , +and 0 on success. +.SH ERRORS +The +.I db_open +function may fail and set +.I errno +for any of the errors specified for the library functions +.IR open (2), +.IR malloc (3) +or the following: +.TP 5 +[EFTYPE] +A file is incorrectly formatted. +.TP 5 +[EINVAL] +A parameter has been specified (hash function, recno pad byte etc.) +that is incompatible with the current file specification or, a flag +to a function which is not meaningful for the function (for example, +use of the cursor without prior initialization) or there is a mismatch +between the version number of file and the software. +.PP +The +.I close +functions may fail and set +.I errno +for any of the errors specified for the library functions +.IR close (2), +.IR read (2), +.IR write (2), +.IR free (3), +or +.IR fsync (2). +.PP +The +.IR del , +.IR get , +.I put +and +.I seq +functions may fail and set +.I errno +for any of the errors specified for the library functions +.IR read (2), +.IR write (2), +.IR free (3) +or +.IR malloc (3). +.PP +The +.I fd +functions will fail and set +.I errno +to ENOENT for in memory databases. +.PP +The +.I sync +functions may fail and set +.I errno +for any of the errors specified for the library function +.IR fsync (2). +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_recno (3), +.IR db_txn (3) +.SH BUGS +The name DBT is a mnemonic for ``data base thang'', and was used +because noone could think of a reasonable name that wasn't already +in use somewhere else. +.PP +The +.I fd +function interface is a kluge, +and will be deleted in a future version of the interface. +.PP +Only big and little endian byte order is supported. diff --git a/src/util/db2/man/db_recno.3 b/src/util/db2/man/db_recno.3 new file mode 100644 index 000000000..6b93b3f5a --- /dev/null +++ b/src/util/db2/man/db_recno.3 @@ -0,0 +1,268 @@ +.\" Copyright (c) 1990, 1993, 1994, 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_recno.3 8.12 (Berkeley) 8/1/95 +.\" +.TH DB_RECNO 3 "August 1, 1995" +.UC 7 +.SH NAME +db_recno \- record number database access method +.SH DESCRIPTION +.so db.so +specific details of the recno access method. +.SH "ACCESS METHOD SPECIFIC INFORMATION" +The recno access method specific data structure provided to +.I db_open +is typedef'd and named RECNOINFO. +A RECNOINFO structure has at least the following fields, +which may be initialized before calling +.IR db_open : +.TP 5 +u_int8_t bval; +The delimiting byte to be used to mark the end of a record for +variable-length records, and the pad character for fixed-length +records. +If no value is specified, newlines (``\en'') are used to mark the end +of variable-length records and fixed-length records are padded with +spaces. +.TP 5 +char *bfname; +The recno access method stores the in-memory copies of its records +in a btree. +If bfname is non-NULL, it specifies the name of the btree file, +as if specified as the file name for a +.I db_open +of a btree file. +.TP 5 +u_int cachesize; +A suggested maximum size, in bytes, of the memory cache. +This value is +.B only +advisory, and the access method will allocate more memory rather than fail. +If +.I cachesize +is 0 (no size is specified) a default size is used. +.TP 5 +u_long flags; +The flag value is specified by +.IR or 'ing +any of the following values: +.RS +.TP 5 +R_FIXEDLEN +The records are fixed-length, not byte delimited. +The structure element +.I reclen +specifies the length of the record, and the structure element +.I bval +is used as the pad character. +Any records, inserted into the database, that are less than +.I reclen +bytes long are automatically padded. +.TP 5 +R_NOKEY +In the interface specified by +.IR db_open , +the sequential record retrieval fills in both the caller's key and +data structures. +If the R_NOKEY flag is specified, the +.I cursor +functions are not required to fill in the key structure. +This permits applications to retrieve records at the end of files without +reading all of the intervening records. +.TP 5 +R_SNAPSHOT +This flag requires that a snapshot of the file be taken when +.I db_open +is called, instead of permitting any unmodified records to be read from +the original file. +.RE +.TP 5 +int lorder; +The byte order for integers in the stored database metadata. +The number should represent the order as an integer; for example, +big endian order would be the number 4,321. +If +.I lorder +is 0 (no order is specified) the current host order is used. +.TP 5 +u_int psize; +The recno access method stores the in-memory copies of its records +in a btree. +This value is the size (in bytes) of the pages used for nodes in that tree. +If +.I psize +is 0 (no page size is specified) a page size is chosen based on the +underlying file system I/O block size. +See +.IR btree (3) +for more information. +.TP 5 +size_t reclen; +The length of a fixed-length record. +.SH "DB OPERATIONS" +The data part of the key/data pair used by the recno access method +is the same as other access methods. +The key is different. +The +.I data +field of the key should be a pointer to a memory location of type +.IR recno_t , +as defined in the include file. +This type is normally the largest unsigned integral type available to +the implementation. +The +.I size +field of the key should be the size of that type. +.PP +The record number data structure is either variable or fixed-length +records stored in a flat-file format, accessed by the logical record +number. +The existence of record number five requires the existence of records +one through four, and the deletion of record number one causes +record number five to be renumbered to record number four, as well +as the cursor, if positioned after record number one, to shift down +one record. +The creation of record number five when records one through four do +not exist causes the logical creation of them with zero-length data. +.PP +Because there is no meta-data associated with the underlying recno access +method files, any changes made to the default values (e.g. fixed record +length or byte separator value) must be explicitly specified each time the +file is opened. +.PP +The functions returned by +.I db_open +for the btree access method are as described in +.IR db_open (3), +with the following exceptions and additions: +.TP 5 +type +The type is DB_RECNO. +.TP 5 +put +Using the +.I put +interface to create a new record will cause the creation of multiple, +empty records if the record number is more than one greater than the +largest record currently in the database. +.IP +The +.I put +function takes the following additional flags: +.RS +.TP 5 +R_IAFTER +Append the data immediately after the data referenced by +.IR key , +creating a new key/data pair. +The record number of the appended key/data pair is returned in the +.I key +structure. +.TP 5 +R_IBEFORE +Insert the data immediately before the data referenced by +.IR key , +creating a new key/data pair. +The record number of the inserted key/data pair is returned in the +.I key +structure. +.TP 5 +R_SETCURSOR +Store the key/data pair, setting or initializing the position of the +cursor to reference it. +.RE +.TP 5 +seq +The +.I seq +function takes the following additional flags: +.RS +.TP 5 +R_LAST +The last key/data pair of the database is returned, and the cursor +is set or initialized to reference it. +.TP 5 +R_PREV +Retrieve the key/data pair immediately before the cursor. +If the cursor is not yet set, this is the same as the R_LAST flag. +.RE +.IP +If the database file is a character special file and no complete +key/data pairs are currently available, the +.I seq +function returns 2. +.TP 5 +sync +The +.I sync +function takes the following additional flag: +.RS +.TP 5 +R_RECNOSYNC +This flag causes the +.I sync +function to apply to the btree file which underlies the recno file, +not the recno file itself. +(See the +.I bfname +field of RECNOINFO +structure, above, for more information.) +.RE +.SH ERRORS +The +.I recno +access method functions may fail and set +.I errno +for any of the errors specified for the library function +.IR db_open (3) +or the following: +.TP 5 +[EINVAL] +An attempt was made to add a record to a fixed-length database that +was too large to fit. +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_txn (3) +.sp +.IR "Document Processing in a Relational Database System" , +Michael Stonebraker, Heidi Stettner, Joseph Kalash, Antonin Guttman, +Nadene Lynn, Memorandum No. UCB/ERL M82/32, May 1982. +.SH BUGS +The +.I sync +function's R_RECNOSYNC interface is a kluge, +and will be deleted in a future version of the interface. diff --git a/src/util/db2/man/db_txn.3 b/src/util/db2/man/db_txn.3 new file mode 100644 index 000000000..18ad64692 --- /dev/null +++ b/src/util/db2/man/db_txn.3 @@ -0,0 +1,373 @@ +.\" Copyright (c) 1994, 1995 +.\" The President and Fellows of Harvard University. All rights reserved. +.\" Copyright (c) 1994, 1995 +.\" Margo I. Selzer. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)db_txn.3 8.8 (Harvard) 8/1/95 +.\" +.TH DB_TXN 3 "August 1, 1995" +.UC 7 +.SH NAME +db_txn \- transaction management functions +.SH SYNOPSIS +.nf +.ft B +#include +#include + +int +txn_create(const char *path, mode_t mode, u_int maxtxns, u_int flags); + +TXNMGR * +txn_open(const char *path, DBT *logp, LOCK_TABLE_T *lockp, +.ti +5 +int (*recover)(DBT *lsn, DBT *log_entry, int isundo)); + +TXN * +txn_begin(TXNMGR *txnp); + +int +txn_commit(TXN *tid); + +int +txn_prepare(TXN *tid); + +int +txn_abort(TXN *tid); + +int +txn_close(TXNMGR *txnp); + +int +txn_unlink(const char *path, int force); +.ft R +.fi +.SH DESCRIPTION +.so db.so +specific details of the transaction support. +.PP +.I Db_txn +is the library interface that provides transaction semantics. +Full transaction support is provided by a collection of modules +that provide well defined interfaces to the services required for +transaction processing. +These services are recovery (see +.IR db_log (3)), +concurrency control (see +.IR db_lock (3)), +and the management of shared data (see +.IR db_mpool (3)). +Transaction semantics can be applied to the access methods described in +.IR db (3) +through function call parameters. +.PP +The model intended for transactional use (and that is used by the +access methods) is that write-ahead logging is provided by +.IR db_log (3) +to record both before- and after-image logging. +Locking follows a two-phase protocol and is implemented by +.IR db_lock (3). +.PP +.CR "transaction region" txn +Any necessary, +associated log and lock regions are created as well (see +.IR db_log (3) +and +.IR db_lock (3)). +.PP +The +.I maxtxns +argument specifies the maximum number of simultaneous transactions that +are supported. +This bounds the size of backing files and is used to derive limits for +the size of the lock region and logfiles. +When there are more than +.I maxtxns +concurrent transactions, calls to +.I txn_begin +may fail. +.PP +Default locking and logging protocols are provided only if the +backing files exist. +If the backing files do not exist, the +.I flags +parameter must indicate both a logging mode and locking mode specified by +.IR or 'ing +together at most one flag from each of the TXN_LOCK and TXN_LOG classes +as follows: +.TP 5 +TXN_LOCK_2PL +Use two-phase locking. +.TP 5 +TXN_LOCK_OPTIMISTIC +Use optimistic locking (not currently implemented). +.TP 5 +TXN_LOG_REDO +Provide redo-only logging (not currently implemented). +.TP 5 +TXN_LOG_UNDO +Provide undo-only logging (not currently implemented). +.TP 5 +TXN_LOG_UNDOREDO +Provide undo/redo write-ahead logging. +.PP +.RT txn_create +.PP +.OP "transaction region" txn +.PP +The +.I recover +argument specifies a function that is called by +.I txn_abort +during transaction abort. +This function takes three arguments: +.TP 5 +lsn +A log sequence number (LSN). +.TP 5 +log_entry +A log record. +.TP 5 +isundo +An undo flag set to 0 if the operation is a redo and set to 1 if the +operation an undo. +.PP +As discussed in the +.I db_log (3) +manual page, +the application is responsible for providing any necessary structure +to the log record. +For example, the application must understand what part of the log +record is an operation code, what part is redo information, and what +part is undo information. +.PP +The +.I txn_begin +function creates a new transaction in the designated transaction +manager, returning a pointer to a TXN that uniquely identifies it. +.PP +The +.I txn_commit +function ends the transaction specified by the +.I tid +argument. +Any locks held by the transaction are released. +If logging is enabled, a commit log record is written and flushed to disk. +.PP +The +.I txn_abort +function causes an abnormal termination of the transaction. +If logging is enabled, the log is played backwards and any recovery +operations are initiated through the +.I recover +function specified to +.IR txn_open . +After recovery is completed, all locks held by the transaction are released. +.PP +The +.I txn_close +function detaches a process from the transaction environment specified +by the TXNMGR pointer. +All mapped regions are unmapped and any allocated resources are freed. +Any uncommitted transactions are aborted. +.PP +.UN "transaction region" txn +.PP +The +.I txn_prepare +function initiates the beginning of a two phase commit. +In a distributed transaction, +the prepare directive should be issued to all participating +transaction managers. +Each manager must take whatever action is necessary to guarantee +that a future call to +.I txn_commit +on the specified +.I tid +will succeed. +.SH "SYSTEM INTEGRATION" +This model can be applied to data bases other than the provided access +methods. +For example, consider an application that provides transaction semantics +to data stored in regular files accessed using the +.IR read (2) +and +.IR write (2) +system calls. +The operations for which transaction protection is desired are bracketed +by calls to +.I txn_begin +and +.IR txn_commit . +.PP +Before data are referenced, a call is made to the lock manager, +.IR db_lock , +for a lock of the appropriate type (e.g. read) +on the object being locked. +The object might be a page in the file, a byte, a range of bytes, +or some key. +Before a write is performed, the application makes a call to the +log manager, +.IR db_log , +to record enough information to redo the operation in case of +failure after commit and to undo the operation in case of abort. +After the log message is written, the write system calls are issued. +After all requests are issued, the application calls +.IR txn_commit . +When +.I txn_commit +returns, the caller is guaranteed that all necessary log writes have +been written to disk. +.PP +At any time, the application may call +.IR txn_abort , +which will result in the appropriate calls to the +.I recover +routine to restore the ``database'' to a consistent pre-transaction +state. +(The recover routine must be able to either reapply or undo the update +depending on the context, for each different type of log record.) +.PP +If the application should crash, the recovery process uses the +.I db_log +interface to read the log and call the +.I recover +routine to restore the database to a consistent state. +.PP +The +.I txn_prepare +function provides the core functionality to implement distributed +transactions, +but it does not actually manage the notification of distributed +transaction managers. +The caller is responsible for issuing +.I txn_prepare +calls to all sites participating in the transaction. +If all responses are positive, the caller can issue a +.IR txn_commit . +If any of the responses are negative, the caller should issue a +.IR txn_abort . +In general, the +.I txn_prepare +call requires that the transaction log be flushed to disk. +.PP +The structure of the transaction support allows application designers +to trade off performance and protection. +Since DB manages many structures in shared memory, +its information is subject to corruption by applications when the library +is linked directly with the application. +For this reason, DB is designed to allow compilation into a separate +server process that may be accessed via a socket interface. +In this way DB's data structures are protected from application code, +but communication overhead is increased. +When applications are trusted, DB may be compiled directly into the +application for increased performance. +.SH ERRORS +The +.I txn_create +function may fail and set +.I errno +for any of the errors specified for the library functions +.IR open (2), +.IR write (2), +.IR malloc (3), +.IR lock_create (3), +and +.IR log_create (3). +.PP +The +.I txn_open +function may fail and set +.I errno +to any of the errors specified for the library functions +.IR open (2), +.IR write (2), +.IR malloc (3), +.IR lock_open (3), +and +.IR log_open (3). +.PP +The +.I txn_begin +function may fail and set +.I errno +to ENOSPC indicating that the maximum number of concurrent +transactions has been reached. +.PP +The +.I txn_commit +function may fail and set +.I errno +to EINVAL indicating that the transaction was aborted. +.PP +The +.I txn_close +function may fail and set +.I errno +to any of the errors specified for the library functions +.IR close (2), +.IR read (2), +.IR write (2), +.IR free (3), +.IR fsync (2), +.IR lock_close (3) +or +.IR log_close (3). +.PP +The +.I txn_unlink +function may fail and set +.I errno +to any of the errors specified for the library functions +.IR unlink (2), +.IR lock_unlink (3), +and +.IR log_unlink (3), +or the following: +.TP 5 +[EBUSY] +The transaction region was in use and the force flag was not set. +.SH "SEE ALSO" +.IR db_btree (3), +.IR db_hash (3), +.IR db_lock (3), +.IR db_log (3), +.IR db_mpool (3), +.IR db_open (3), +.IR db_recno (3) +.sp +.IR "LIBTP: Portable, Modular Transactions for UNIX" , +Margo Seltzer, Michael Olson, USENIX proceedings, Winter 1992. +.SH BUGS +The +.I maxtxns +parameter is a kluge, and should be deleted in favor of dynamically +expanding the transaction region. diff --git a/src/util/db2/man/spell.ok b/src/util/db2/man/spell.ok new file mode 100644 index 000000000..794b00bf8 --- /dev/null +++ b/src/util/db2/man/spell.ok @@ -0,0 +1,170 @@ +Ake +Antonin +BTREE +BTREEINFO +Bsize +CALLBACK +Comput +D.E +DB +DB's +DBINFO +DBT +DBTYPE +Db +EACCES +EBUSY +EFTYPE +EINVAL +ENOENT +ENOSPC +ERL +EXCL +EXLOCK +FIXEDLEN +Ffactor +Guttman +HASHINFO +Heidi +IAFTER +IBEFORE +Kalash +Knuth +LIBTP +LOGINFO +LRU +LSN +LSN's +MPOOL +MPOOL's +MPOOLFILE +MPOOLFILE's +Maxlocks +Mpool +NG +NOKEY +NOOVERWRITE +NOPIN +NOTHELD +Nadene +Nelem +Nelems +OBJ +Pgaddr +RDONLY +RDWR +RECNO +RECNOINFO +RECNOSYNC +REQ +SETCURSOR +SHLOCK +Stettner +Stonebraker +Surv +TMPDIR +TRUNC +TXN +TXNMGR +Txn +UCB +UNDOREDO +USENIX +Unterauer +Vol +WAL +WRONLY +XACT +YYYY.MM.DD.HH.SS +al +bfname +bsize +btree +btrees +bval +cachesize +callback +const +db +db.h +dbinfo +dbopen +del +elistp +endian +enum +errbuf +errfile +errno +errpfx +fd +ffactor +getv +ing +int +int32 +int8 +isundo +kluge +lastlsn +lg +lock.h +lockinfo +lockop +lockp +log.YYYY.MM.DD.HH.MM.SS +logfiles +loginfo +logp +lreq +lsn +lsn1 +lsn2 +lt +maxcache +maxkeypage +maxlocks +maxtxns +meta +minkeypage +mmap +mpf +mpool +mpool.h +mpoolinfo +munmap +nacquire +nelem +nelems +nmodes +noone +nrelease +obj +op +openinfo +pathname +pgaddr +pgcookie +pgin +pgno +pgnoaddr +pgout +pid +pp +psize +queue.h +reclen +recno +sx +thang +timespec +tmp +trunc +txn +txnid +txninfo +txnp +typedef +typedef'd +vec +writeable diff --git a/src/util/db2/mpool/Makefile.inc b/src/util/db2/mpool/Makefile.inc new file mode 100644 index 000000000..93210c89e --- /dev/null +++ b/src/util/db2/mpool/Makefile.inc @@ -0,0 +1,5 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/mpool + +SRCS+= mpool.c diff --git a/src/util/db2/mpool/README b/src/util/db2/mpool/README new file mode 100644 index 000000000..0f01fbcdb --- /dev/null +++ b/src/util/db2/mpool/README @@ -0,0 +1,7 @@ +# @(#)README 8.1 (Berkeley) 6/4/93 + +These are the current memory pool routines. +They aren't ready for prime time, yet, and +the interface is expected to change. + +--keith diff --git a/src/util/db2/mpool/mpool.c b/src/util/db2/mpool/mpool.c new file mode 100644 index 000000000..12e557d03 --- /dev/null +++ b/src/util/db2/mpool/mpool.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)mpool.c 8.7 (Berkeley) 11/2/95"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "db-int.h" +#include "mpool.h" + +static BKT *mpool_bkt __P((MPOOL *)); +static BKT *mpool_look __P((MPOOL *, db_pgno_t)); +static int mpool_write __P((MPOOL *, BKT *)); + +/* + * mpool_open -- + * Initialize a memory pool. + */ +MPOOL * +mpool_open(key, fd, pagesize, maxcache) + void *key; + int fd; + db_pgno_t pagesize, maxcache; +{ + struct stat sb; + MPOOL *mp; + int entry; + + /* + * Get information about the file. + * + * XXX + * We don't currently handle pipes, although we should. + */ + if (fstat(fd, &sb)) + return (NULL); + if (!S_ISREG(sb.st_mode)) { + errno = ESPIPE; + return (NULL); + } + + /* Allocate and initialize the MPOOL cookie. */ + if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL) + return (NULL); + CIRCLEQ_INIT(&mp->lqh); + for (entry = 0; entry < HASHSIZE; ++entry) + CIRCLEQ_INIT(&mp->hqh[entry]); + mp->maxcache = maxcache; + mp->npages = sb.st_size / pagesize; + mp->pagesize = pagesize; + mp->fd = fd; + return (mp); +} + +/* + * mpool_filter -- + * Initialize input/output filters. + */ +void +mpool_filter(mp, pgin, pgout, pgcookie) + MPOOL *mp; + void (*pgin) __P((void *, db_pgno_t, void *)); + void (*pgout) __P((void *, db_pgno_t, void *)); + void *pgcookie; +{ + mp->pgin = pgin; + mp->pgout = pgout; + mp->pgcookie = pgcookie; +} + +/* + * mpool_new -- + * Get a new page of memory. + */ +void * +mpool_new(mp, pgnoaddr, flags) + MPOOL *mp; + db_pgno_t *pgnoaddr; + u_int flags; +{ + struct _hqh *head; + BKT *bp; + + if (mp->npages == MAX_PAGE_NUMBER) { + (void)fprintf(stderr, "mpool_new: page allocation overflow.\n"); + abort(); + } +#ifdef STATISTICS + ++mp->pagenew; +#endif + /* + * Get a BKT from the cache. Assign a new page number, attach + * it to the head of the hash chain, the tail of the lru chain, + * and return. + */ + if ((bp = mpool_bkt(mp)) == NULL) + return (NULL); + if (flags == MPOOL_PAGE_REQUEST) { + mp->npages++; + bp->pgno = *pgnoaddr; + } else + bp->pgno = *pgnoaddr = mp->npages++; + + bp->flags = MPOOL_PINNED | MPOOL_INUSE; + + head = &mp->hqh[HASHKEY(bp->pgno)]; + CIRCLEQ_INSERT_HEAD(head, bp, hq); + CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); + return (bp->page); +} + +int +mpool_delete(mp, page) + MPOOL *mp; + void *page; +{ + struct _hqh *head; + BKT *bp; + + bp = (BKT *)((char *)page - sizeof(BKT)); + +#ifdef DEBUG + if (!(bp->flags & MPOOL_PINNED)) { + (void)fprintf(stderr, + "mpool_delete: page %d not pinned\n", bp->pgno); + abort(); + } +#endif + + /* Remove from the hash and lru queues. */ + head = &mp->hqh[HASHKEY(bp->pgno)]; + CIRCLEQ_REMOVE(head, bp, hq); + CIRCLEQ_REMOVE(&mp->lqh, bp, q); + + free(bp); + return (RET_SUCCESS); +} + +/* + * mpool_get + * Get a page. + */ +void * +mpool_get(mp, pgno, flags) + MPOOL *mp; + db_pgno_t pgno; + u_int flags; /* XXX not used? */ +{ + struct _hqh *head; + BKT *bp; + off_t off; + int nr; + +#ifdef STATISTICS + ++mp->pageget; +#endif + + /* Check for a page that is cached. */ + if ((bp = mpool_look(mp, pgno)) != NULL) { +#ifdef DEBUG + if (!(flags & MPOOL_IGNOREPIN) && bp->flags & MPOOL_PINNED) { + (void)fprintf(stderr, + "mpool_get: page %d already pinned\n", bp->pgno); + abort(); + } +#endif + /* + * Move the page to the head of the hash chain and the tail + * of the lru chain. + */ + head = &mp->hqh[HASHKEY(bp->pgno)]; + CIRCLEQ_REMOVE(head, bp, hq); + CIRCLEQ_INSERT_HEAD(head, bp, hq); + CIRCLEQ_REMOVE(&mp->lqh, bp, q); + CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); + + /* Return a pinned page. */ + bp->flags |= MPOOL_PINNED; + return (bp->page); + } + + /* Get a page from the cache. */ + if ((bp = mpool_bkt(mp)) == NULL) + return (NULL); + + /* Read in the contents. */ +#ifdef STATISTICS + ++mp->pageread; +#endif + off = mp->pagesize * pgno; + if (lseek(mp->fd, off, SEEK_SET) != off) + return (NULL); + + if ((nr = read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) { + if (nr > 0) { + /* A partial read is definitely bad. */ + errno = EINVAL; + return (NULL); + } else { + /* + * A zero-length reads, means you need to create a + * new page. + */ + memset(bp->page, 0, mp->pagesize); + } + } + + /* Set the page number, pin the page. */ + bp->pgno = pgno; + if (!(flags & MPOOL_IGNOREPIN)) + bp->flags = MPOOL_PINNED; + bp->flags |= MPOOL_INUSE; + + /* + * Add the page to the head of the hash chain and the tail + * of the lru chain. + */ + head = &mp->hqh[HASHKEY(bp->pgno)]; + CIRCLEQ_INSERT_HEAD(head, bp, hq); + CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q); + + /* Run through the user's filter. */ + if (mp->pgin != NULL) + (mp->pgin)(mp->pgcookie, bp->pgno, bp->page); + + return (bp->page); +} + +/* + * mpool_put + * Return a page. + */ +int +mpool_put(mp, page, flags) + MPOOL *mp; + void *page; + u_int flags; +{ + BKT *bp; + +#ifdef STATISTICS + ++mp->pageput; +#endif + bp = (BKT *)((char *)page - sizeof(BKT)); +#ifdef DEBUG + if (!(bp->flags & MPOOL_PINNED)) { + (void)fprintf(stderr, + "mpool_put: page %d not pinned\n", bp->pgno); + abort(); + } +#endif + bp->flags &= ~MPOOL_PINNED; + if (flags & MPOOL_DIRTY) + bp->flags |= flags & MPOOL_DIRTY; + return (RET_SUCCESS); +} + +/* + * mpool_close + * Close the buffer pool. + */ +int +mpool_close(mp) + MPOOL *mp; +{ + BKT *bp; + + /* Free up any space allocated to the lru pages. */ + while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) { + CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q); + free(bp); + } + + /* Free the MPOOL cookie. */ + free(mp); + return (RET_SUCCESS); +} + +/* + * mpool_sync + * Sync the pool to disk. + */ +int +mpool_sync(mp) + MPOOL *mp; +{ + BKT *bp; + + /* Walk the lru chain, flushing any dirty pages to disk. */ + for (bp = mp->lqh.cqh_first; + bp != (void *)&mp->lqh; bp = bp->q.cqe_next) + if (bp->flags & MPOOL_DIRTY && + mpool_write(mp, bp) == RET_ERROR) + return (RET_ERROR); + + /* Sync the file descriptor. */ + return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS); +} + +/* + * mpool_bkt + * Get a page from the cache (or create one). + */ +static BKT * +mpool_bkt(mp) + MPOOL *mp; +{ + struct _hqh *head; + BKT *bp; + + /* If under the max cached, always create a new page. */ + if (mp->curcache < mp->maxcache) + goto new; + + /* + * If the cache is max'd out, walk the lru list for a buffer we + * can flush. If we find one, write it (if necessary) and take it + * off any lists. If we don't find anything we grow the cache anyway. + * The cache never shrinks. + */ + for (bp = mp->lqh.cqh_first; + bp != (void *)&mp->lqh; bp = bp->q.cqe_next) + if (!(bp->flags & MPOOL_PINNED)) { + /* Flush if dirty. */ + if (bp->flags & MPOOL_DIRTY && + mpool_write(mp, bp) == RET_ERROR) + return (NULL); +#ifdef STATISTICS + ++mp->pageflush; +#endif + /* Remove from the hash and lru queues. */ + head = &mp->hqh[HASHKEY(bp->pgno)]; + CIRCLEQ_REMOVE(head, bp, hq); + CIRCLEQ_REMOVE(&mp->lqh, bp, q); +#ifdef DEBUG + { void *spage; + spage = bp->page; + memset(bp, 0xff, sizeof(BKT) + mp->pagesize); + bp->page = spage; + } +#endif + bp->flags = 0; + return (bp); + } + +new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL) + return (NULL); +#ifdef STATISTICS + ++mp->pagealloc; +#endif +#if defined(DEBUG) || defined(PURIFY) + memset(bp, 0xff, sizeof(BKT) + mp->pagesize); +#endif + bp->page = (char *)bp + sizeof(BKT); + bp->flags = 0; + ++mp->curcache; + return (bp); +} + +/* + * mpool_write + * Write a page to disk. + */ +static int +mpool_write(mp, bp) + MPOOL *mp; + BKT *bp; +{ + off_t off; + +#ifdef STATISTICS + ++mp->pagewrite; +#endif + + /* Run through the user's filter. */ + if (mp->pgout) + (mp->pgout)(mp->pgcookie, bp->pgno, bp->page); + + off = mp->pagesize * bp->pgno; + if (lseek(mp->fd, off, SEEK_SET) != off) + return (RET_ERROR); + if (write(mp->fd, bp->page, mp->pagesize) != mp->pagesize) + return (RET_ERROR); + + bp->flags &= ~MPOOL_DIRTY; + return (RET_SUCCESS); +} + +/* + * mpool_look + * Lookup a page in the cache. + */ +static BKT * +mpool_look(mp, pgno) + MPOOL *mp; + db_pgno_t pgno; +{ + struct _hqh *head; + BKT *bp; + + head = &mp->hqh[HASHKEY(pgno)]; + for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next) + if ((bp->pgno == pgno) && + (bp->flags & MPOOL_INUSE == MPOOL_INUSE)) { +#ifdef STATISTICS + ++mp->cachehit; +#endif + return (bp); + } +#ifdef STATISTICS + ++mp->cachemiss; +#endif + return (NULL); +} + +#ifdef STATISTICS +/* + * mpool_stat + * Print out cache statistics. + */ +void +mpool_stat(mp) + MPOOL *mp; +{ + BKT *bp; + int cnt; + char *sep; + + (void)fprintf(stderr, "%lu pages in the file\n", mp->npages); + (void)fprintf(stderr, + "page size %lu, cacheing %lu pages of %lu page max cache\n", + mp->pagesize, mp->curcache, mp->maxcache); + (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n", + mp->pageput, mp->pageget, mp->pagenew); + (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n", + mp->pagealloc, mp->pageflush); + if (mp->cachehit + mp->cachemiss) + (void)fprintf(stderr, + "%.0f%% cache hit rate (%lu hits, %lu misses)\n", + ((double)mp->cachehit / (mp->cachehit + mp->cachemiss)) + * 100, mp->cachehit, mp->cachemiss); + (void)fprintf(stderr, "%lu page reads, %lu page writes\n", + mp->pageread, mp->pagewrite); + + sep = ""; + cnt = 0; + for (bp = mp->lqh.cqh_first; + bp != (void *)&mp->lqh; bp = bp->q.cqe_next) { + (void)fprintf(stderr, "%s%d", sep, bp->pgno); + if (bp->flags & MPOOL_DIRTY) + (void)fprintf(stderr, "d"); + if (bp->flags & MPOOL_PINNED) + (void)fprintf(stderr, "P"); + if (++cnt == 10) { + sep = "\n"; + cnt = 0; + } else + sep = ", "; + + } + (void)fprintf(stderr, "\n"); +} +#endif diff --git a/src/util/db2/mpool/mpool.h b/src/util/db2/mpool/mpool.h new file mode 100644 index 000000000..92bf7541d --- /dev/null +++ b/src/util/db2/mpool/mpool.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)mpool.h 8.4 (Berkeley) 11/2/95 + */ + +#include "db-queue.h" + +/* + * The memory pool scheme is a simple one. Each in-memory page is referenced + * by a bucket which is threaded in up to two of three ways. All active pages + * are threaded on a hash chain (hashed by page number) and an lru chain. + * Inactive pages are threaded on a free chain. Each reference to a memory + * pool is handed an opaque MPOOL cookie which stores all of this information. + */ +#define HASHSIZE 128 +#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE) + +/* The BKT structures are the elements of the queues. */ +typedef struct _bkt { + CIRCLEQ_ENTRY(_bkt) hq; /* hash queue */ + CIRCLEQ_ENTRY(_bkt) q; /* lru queue */ + void *page; /* page */ + db_pgno_t pgno; /* page number */ + +#define MPOOL_DIRTY 0x01 /* page needs to be written */ +#define MPOOL_PINNED 0x02 /* page is pinned into memory */ +#define MPOOL_INUSE 0x04 /* page address is valid */ + u_int8_t flags; /* flags */ +} BKT; + +typedef struct MPOOL { + CIRCLEQ_HEAD(_lqh, _bkt) lqh; /* lru queue head */ + /* hash queue array */ + CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE]; + db_pgno_t curcache; /* current number of cached pages */ + db_pgno_t maxcache; /* max number of cached pages */ + db_pgno_t npages; /* number of pages in the file */ + u_long pagesize; /* file page size */ + int fd; /* file descriptor */ + /* page in conversion routine */ + void (*pgin) __P((void *, db_pgno_t, void *)); + /* page out conversion routine */ + void (*pgout) __P((void *, db_pgno_t, void *)); + void *pgcookie; /* cookie for page in/out routines */ +#ifdef STATISTICS + u_long cachehit; + u_long cachemiss; + u_long pagealloc; + u_long pageflush; + u_long pageget; + u_long pagenew; + u_long pageput; + u_long pageread; + u_long pagewrite; +#endif +} MPOOL; + +#define MPOOL_IGNOREPIN 0x01 /* Ignore if the page is pinned. */ +#define MPOOL_PAGE_REQUEST 0x01 /* Allocate a new page with a + specific page number. */ +#define MPOOL_PAGE_NEXT 0x02 /* Allocate a new page with the next + page number. */ + +__BEGIN_DECLS +MPOOL *mpool_open __P((void *, int, db_pgno_t, db_pgno_t)); +void mpool_filter __P((MPOOL *, void (*)(void *, db_pgno_t, void *), + void (*)(void *, db_pgno_t, void *), void *)); +void *mpool_new __P((MPOOL *, db_pgno_t *, u_int)); +void *mpool_get __P((MPOOL *, db_pgno_t, u_int)); +int mpool_delete __P((MPOOL *, void *)); +int mpool_put __P((MPOOL *, void *, u_int)); +int mpool_sync __P((MPOOL *)); +int mpool_close __P((MPOOL *)); +#ifdef STATISTICS +void mpool_stat __P((MPOOL *)); +#endif +__END_DECLS diff --git a/src/util/db2/obj/Makefile.in b/src/util/db2/obj/Makefile.in new file mode 100644 index 000000000..5e60ef796 --- /dev/null +++ b/src/util/db2/obj/Makefile.in @@ -0,0 +1,157 @@ +SHELL = /bin/sh + +LIBDB = libdb.a + +HASH_OBJS = hash.o hash_bigkey.o hash_debug.o hash_func.o hash_log2.o \ + hash_page.o hsearch.o dbm.o +BT_OBJS = bt_close.o bt_conv.o bt_debug.o bt_delete.o bt_get.o \ + bt_open.o bt_overflow.o bt_page.o bt_put.o bt_search.o \ + bt_seq.o bt_split.o bt_utils.o +DB_OBJS = db.o +MPOOL_OBJS = mpool.o +REC_OBJS = rec_close.o rec_delete.o rec_get.o rec_open.o rec_put.o \ + rec_search.o rec_seq.o rec_utils.o + +MEMMOVE_OBJ = @MEMMOVE_OBJ@ +MKSTEMP_OBJ = @MKSTEMP_OBJ@ +STRERROR_OBJ = @STRERROR_OBJ@ + +MISC_OBJS = $(MEMMOVE_OBJ) $(MKSTEMP_OBJ) + +ALL_OBJS = $(HASH_OBJS) $(BT_OBJS) $(DB_OBJS) $(MPOOL_OBJS) \ + $(REC_OBJS) $(MISC_OBJS) + +TMPDIR = /tmp + +AR = ar +CC = @CC@ +RANLIB = @RANLIB@ +FCTSH = @FCTSH@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +includedir = @includedir@ +libdir = @libdir@ + +CDEBUGFLAGS = @CFLAGS@ + +CFLAGS = $(CDEBUGFLAGS) @CPPFLAGS@ @DEFS@ \ + -I. -I$(top_srcdir)/include -I$(top_srcdir)/mpool -I$(top_srcdir)/db \ + -I$(top_srcdir)/hash -I$(top_srcdir)/btree -I$(top_srcdir)/recno + +all:: $(LIBDB) db.h + +$(LIBDB): $(ALL_OBJS) + $(AR) cru $@ $(ALL_OBJS) + $(RANLIB) $@ + +db.h: $(top_srcdir)/include/db.h + ln -s $(top_srcdir)/include/db.h . + +dbtest: dbtest.o $(STRERROR_OBJ) $(LIBDB) + $(CC) -o $@ dbtest.o $(STRERROR_OBJ) $(LIBDB) + +check:: dbtest + TMPDIR=$(TMPDIR) $(FCTSH) $(top_srcdir)/test/run.test + +install:: + cp $(LIBDB) $(libdir) + $(RANLIB) $(libdir)/$(LIBDB) + cp $(top_srcdir)/include/db.h $(includedir) + cp ../db-config.h $(includedir) + +clean:: + rm -f $(ALL_OBJS) $(LIBDB) \ + dbtest.o $(STRERROR_OBJ) dbtest \ + core t1 t2 t3 *~ + +## if VPATH worked everywhere, I could do this: +## +## VPATH = @top_srcdir@/db @top_srcdir@/mpool \ +## @top_srcdir@/hash @top_srcdir@/btree @top_srcdir@/recno \ +## @top_srcdir@/clib +## +## but it doesn't, so I do this instead: + +db.o: $(top_srcdir)/db/db.c + $(CC) $(CFLAGS) -c -o db.o $(top_srcdir)/db/db.c + +mpool.o: $(top_srcdir)/mpool/mpool.c + $(CC) $(CFLAGS) -c -o mpool.o $(top_srcdir)/mpool/mpool.c + +hash.o: $(top_srcdir)/hash/hash.c + $(CC) $(CFLAGS) -c -o hash.o $(top_srcdir)/hash/hash.c +hash_bigkey.o: $(top_srcdir)/hash/hash_bigkey.c + $(CC) $(CFLAGS) -c -o hash_bigkey.o $(top_srcdir)/hash/hash_bigkey.c +hash_debug.o: $(top_srcdir)/hash/hash_debug.c + $(CC) $(CFLAGS) -c -o hash_debug.o $(top_srcdir)/hash/hash_debug.c +hash_func.o: $(top_srcdir)/hash/hash_func.c + $(CC) $(CFLAGS) -c -o hash_func.o $(top_srcdir)/hash/hash_func.c +hash_log2.o: $(top_srcdir)/hash/hash_log2.c + $(CC) $(CFLAGS) -c -o hash_log2.o $(top_srcdir)/hash/hash_log2.c +hash_page.o: $(top_srcdir)/hash/hash_page.c + $(CC) $(CFLAGS) -c -o hash_page.o $(top_srcdir)/hash/hash_page.c +hsearch.o: $(top_srcdir)/hash/hsearch.c + $(CC) $(CFLAGS) -c -o hsearch.o $(top_srcdir)/hash/hsearch.c +dbm.o: $(top_srcdir)/hash/dbm.c + $(CC) $(CFLAGS) -c -o dbm.o $(top_srcdir)/hash/dbm.c + +bt_close.o: $(top_srcdir)/btree/bt_close.c + $(CC) $(CFLAGS) -c -o bt_close.o $(top_srcdir)/btree/bt_close.c +bt_conv.o: $(top_srcdir)/btree/bt_conv.c + $(CC) $(CFLAGS) -c -o bt_conv.o $(top_srcdir)/btree/bt_conv.c +bt_debug.o: $(top_srcdir)/btree/bt_debug.c + $(CC) $(CFLAGS) -c -o bt_debug.o $(top_srcdir)/btree/bt_debug.c +bt_delete.o: $(top_srcdir)/btree/bt_delete.c + $(CC) $(CFLAGS) -c -o bt_delete.o $(top_srcdir)/btree/bt_delete.c +bt_get.o: $(top_srcdir)/btree/bt_get.c + $(CC) $(CFLAGS) -c -o bt_get.o $(top_srcdir)/btree/bt_get.c +bt_open.o: $(top_srcdir)/btree/bt_open.c + $(CC) $(CFLAGS) -c -o bt_open.o $(top_srcdir)/btree/bt_open.c +bt_overflow.o: $(top_srcdir)/btree/bt_overflow.c + $(CC) $(CFLAGS) -c -o bt_overflow.o $(top_srcdir)/btree/bt_overflow.c +bt_page.o: $(top_srcdir)/btree/bt_page.c + $(CC) $(CFLAGS) -c -o bt_page.o $(top_srcdir)/btree/bt_page.c +bt_put.o: $(top_srcdir)/btree/bt_put.c + $(CC) $(CFLAGS) -c -o bt_put.o $(top_srcdir)/btree/bt_put.c +bt_search.o: $(top_srcdir)/btree/bt_search.c + $(CC) $(CFLAGS) -c -o bt_search.o $(top_srcdir)/btree/bt_search.c +bt_seq.o: $(top_srcdir)/btree/bt_seq.c + $(CC) $(CFLAGS) -c -o bt_seq.o $(top_srcdir)/btree/bt_seq.c +bt_split.o: $(top_srcdir)/btree/bt_split.c + $(CC) $(CFLAGS) -c -o bt_split.o $(top_srcdir)/btree/bt_split.c +bt_stack.o: $(top_srcdir)/btree/bt_stack.c + $(CC) $(CFLAGS) -c -o bt_stack.o $(top_srcdir)/btree/bt_stack.c +bt_utils.o: $(top_srcdir)/btree/bt_utils.c + $(CC) $(CFLAGS) -c -o bt_utils.o $(top_srcdir)/btree/bt_utils.c + +rec_close.o: $(top_srcdir)/recno/rec_close.c + $(CC) $(CFLAGS) -c -o rec_close.o $(top_srcdir)/recno/rec_close.c +rec_delete.o: $(top_srcdir)/recno/rec_delete.c + $(CC) $(CFLAGS) -c -o rec_delete.o $(top_srcdir)/recno/rec_delete.c +rec_get.o: $(top_srcdir)/recno/rec_get.c + $(CC) $(CFLAGS) -c -o rec_get.o $(top_srcdir)/recno/rec_get.c +rec_open.o: $(top_srcdir)/recno/rec_open.c + $(CC) $(CFLAGS) -c -o rec_open.o $(top_srcdir)/recno/rec_open.c +rec_put.o: $(top_srcdir)/recno/rec_put.c + $(CC) $(CFLAGS) -c -o rec_put.o $(top_srcdir)/recno/rec_put.c +rec_search.o: $(top_srcdir)/recno/rec_search.c + $(CC) $(CFLAGS) -c -o rec_search.o $(top_srcdir)/recno/rec_search.c +rec_seq.o: $(top_srcdir)/recno/rec_seq.c + $(CC) $(CFLAGS) -c -o rec_seq.o $(top_srcdir)/recno/rec_seq.c +rec_utils.o: $(top_srcdir)/recno/rec_utils.c + $(CC) $(CFLAGS) -c -o rec_utils.o $(top_srcdir)/recno/rec_utils.c + +dbtest.o: $(top_srcdir)/test/dbtest.c + $(CC) $(CFLAGS) -c -o dbtest.o $(top_srcdir)/test/dbtest.c + +memmove.o: $(top_srcdir)/clib/memmove.c + $(CC) $(CFLAGS) -c -o memmove.o $(top_srcdir)/clib/memmove.c +mkstemp.o: $(top_srcdir)/clib/mkstemp.c + $(CC) $(CFLAGS) -c -o mkstemp.o $(top_srcdir)/clib/mkstemp.c +strerror.o: $(top_srcdir)/clib/strerror.c + $(CC) $(CFLAGS) -c -o strerror.o $(top_srcdir)/clib/strerror.c + +distclean:: clean + rm -f Makefile diff --git a/src/util/db2/recno/Makefile.inc b/src/util/db2/recno/Makefile.inc new file mode 100644 index 000000000..e49e22552 --- /dev/null +++ b/src/util/db2/recno/Makefile.inc @@ -0,0 +1,6 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +.PATH: ${.CURDIR}/db/recno + +SRCS+= rec_close.c rec_delete.c rec_get.c rec_open.c rec_put.c rec_search.c \ + rec_seq.c rec_utils.c diff --git a/src/util/db2/recno/extern.h b/src/util/db2/recno/extern.h new file mode 100644 index 000000000..feed43445 --- /dev/null +++ b/src/util/db2/recno/extern.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)extern.h 8.3 (Berkeley) 6/4/94 + */ + +#include "../btree/extern.h" + +int __rec_close __P((DB *)); +int __rec_delete __P((const DB *, const DBT *, u_int)); +int __rec_dleaf __P((BTREE *, PAGE *, u_int32_t)); +int __rec_fd __P((const DB *)); +int __rec_fmap __P((BTREE *, recno_t)); +int __rec_fout __P((BTREE *)); +int __rec_fpipe __P((BTREE *, recno_t)); +int __rec_get __P((const DB *, const DBT *, DBT *, u_int)); +int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int)); +int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int)); +int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *)); +EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP)); +int __rec_seq __P((const DB *, DBT *, DBT *, u_int)); +int __rec_sync __P((const DB *, u_int)); +int __rec_vmap __P((BTREE *, recno_t)); +int __rec_vout __P((BTREE *)); +int __rec_vpipe __P((BTREE *, recno_t)); diff --git a/src/util/db2/recno/rec_close.c b/src/util/db2/recno/rec_close.c new file mode 100644 index 000000000..2e923070b --- /dev/null +++ b/src/util/db2/recno/rec_close.c @@ -0,0 +1,186 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_close.c 8.9 (Berkeley) 11/18/94"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#ifdef RECNO_USE_MMAP +#include +#endif + +#include +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __REC_CLOSE -- Close a recno tree. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_close(dbp) + DB *dbp; +{ + BTREE *t; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + if (__rec_sync(dbp, 0) == RET_ERROR) + return (RET_ERROR); + + /* Committed to closing. */ + status = RET_SUCCESS; +#ifdef RECNO_USE_MMAP + if (F_ISSET(t, R_MEMMAPPED) && munmap(t->bt_smap, t->bt_msize)) + status = RET_ERROR; +#endif + + if (!F_ISSET(t, R_INMEM)) + if (F_ISSET(t, R_CLOSEFP)) { + if (fclose(t->bt_rfp)) + status = RET_ERROR; + } else + if (close(t->bt_rfd)) + status = RET_ERROR; + + if (__bt_close(dbp) == RET_ERROR) + status = RET_ERROR; + + return (status); +} + +/* + * __REC_SYNC -- sync the recno tree to disk. + * + * Parameters: + * dbp: pointer to access method + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_sync(dbp, flags) + const DB *dbp; + u_int flags; +{ + struct iovec iov[2]; + BTREE *t; + DBT data, key; + off_t off; + recno_t scursor, trec; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + if (flags == R_RECNOSYNC) + return (__bt_sync(dbp, 0)); + + if (F_ISSET(t, R_RDONLY | R_INMEM) || !F_ISSET(t, R_MODIFIED)) + return (RET_SUCCESS); + + /* Read any remaining records into the tree. */ + if (!F_ISSET(t, R_EOF) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + return (RET_ERROR); + + /* Rewind the file descriptor. */ + if (lseek(t->bt_rfd, (off_t)0, SEEK_SET) != 0) + return (RET_ERROR); + + /* Save the cursor. */ + scursor = t->bt_cursor.rcursor; + + key.size = sizeof(recno_t); + key.data = &trec; + + if (F_ISSET(t, R_FIXLEN)) { + /* + * We assume that fixed length records are all fixed length. + * Any that aren't are either EINVAL'd or corrected by the + * record put code. + */ + status = (dbp->seq)(dbp, &key, &data, R_FIRST); + while (status == RET_SUCCESS) { + if (write(t->bt_rfd, data.data, data.size) != data.size) + return (RET_ERROR); + status = (dbp->seq)(dbp, &key, &data, R_NEXT); + } + } else { + iov[1].iov_base = &t->bt_bval; + iov[1].iov_len = 1; + + status = (dbp->seq)(dbp, &key, &data, R_FIRST); + while (status == RET_SUCCESS) { + iov[0].iov_base = data.data; + iov[0].iov_len = data.size; + if (writev(t->bt_rfd, iov, 2) != data.size + 1) + return (RET_ERROR); + status = (dbp->seq)(dbp, &key, &data, R_NEXT); + } + } + + /* Restore the cursor. */ + t->bt_cursor.rcursor = scursor; + + if (status == RET_ERROR) + return (RET_ERROR); + if ((off = lseek(t->bt_rfd, (off_t)0, SEEK_CUR)) == -1) + return (RET_ERROR); + if (ftruncate(t->bt_rfd, off)) + return (RET_ERROR); + F_CLR(t, R_MODIFIED); + return (RET_SUCCESS); +} diff --git a/src/util/db2/recno/rec_delete.c b/src/util/db2/recno/rec_delete.c new file mode 100644 index 000000000..35ec7769a --- /dev/null +++ b/src/util/db2/recno/rec_delete.c @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_delete.c 8.7 (Berkeley) 7/14/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +static int rec_rdelete __P((BTREE *, recno_t)); + +/* + * __REC_DELETE -- Delete the item(s) referenced by a key. + * + * Parameters: + * dbp: pointer to access method + * key: key to delete + * flags: R_CURSOR if deleting what the cursor references + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__rec_delete(dbp, key, flags) + const DB *dbp; + const DBT *key; + u_int flags; +{ + BTREE *t; + recno_t nrec; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + switch(flags) { + case 0: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + if (nrec > t->bt_nrecs) + return (RET_SPECIAL); + --nrec; + status = rec_rdelete(t, nrec); + break; + case R_CURSOR: + if (!F_ISSET(&t->bt_cursor, CURS_INIT)) + goto einval; + if (t->bt_nrecs == 0) + return (RET_SPECIAL); + status = rec_rdelete(t, t->bt_cursor.rcursor - 1); + if (status == RET_SUCCESS) + --t->bt_cursor.rcursor; + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + if (status == RET_SUCCESS) + F_SET(t, B_MODIFIED | R_MODIFIED); + return (status); +} + +/* + * REC_RDELETE -- Delete the data matching the specified key. + * + * Parameters: + * tree: tree + * nrec: record to delete + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +static int +rec_rdelete(t, nrec) + BTREE *t; + recno_t nrec; +{ + EPG *e; + PAGE *h; + int status; + + /* Find the record; __rec_search pins the page. */ + if ((e = __rec_search(t, nrec, SDELETE)) == NULL) + return (RET_ERROR); + + /* Delete the record. */ + h = e->page; + status = __rec_dleaf(t, h, e->index); + if (status != RET_SUCCESS) { + mpool_put(t->bt_mp, h, 0); + return (status); + } + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + return (RET_SUCCESS); +} + +/* + * __REC_DLEAF -- Delete a single record from a recno leaf page. + * + * Parameters: + * t: tree + * index: index on current page to delete + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_dleaf(t, h, index) + BTREE *t; + PAGE *h; + u_int32_t index; +{ + RLEAF *rl; + indx_t *ip, cnt, offset; + u_int32_t nbytes; + char *from; + void *to; + + /* + * Delete a record from a recno leaf page. Internal records are never + * deleted from internal pages, regardless of the records that caused + * them to be added being deleted. Pages made empty by deletion are + * not reclaimed. They are, however, made available for reuse. + * + * Pack the remaining entries at the end of the page, shift the indices + * down, overwriting the deleted record and its index. If the record + * uses overflow pages, make them available for reuse. + */ + to = rl = GETRLEAF(h, index); + if (rl->flags & P_BIGDATA && __ovfl_delete(t, rl->bytes) == RET_ERROR) + return (RET_ERROR); + nbytes = NRLEAF(rl); + + /* + * Compress the key/data pairs. Compress and adjust the [BR]LEAF + * offsets. Reset the headers. + */ + from = (char *)h + h->upper; + memmove(from + nbytes, from, (char *)to - from); + h->upper += nbytes; + + offset = h->linp[index]; + for (cnt = &h->linp[index] - (ip = &h->linp[0]); cnt--; ++ip) + if (ip[0] < offset) + ip[0] += nbytes; + for (cnt = &h->linp[NEXTINDEX(h)] - ip; --cnt; ++ip) + ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1]; + h->lower -= sizeof(indx_t); + --t->bt_nrecs; + return (RET_SUCCESS); +} diff --git a/src/util/db2/recno/rec_get.c b/src/util/db2/recno/rec_get.c new file mode 100644 index 000000000..230b2d4f5 --- /dev/null +++ b/src/util/db2/recno/rec_get.c @@ -0,0 +1,311 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_get.c 8.9 (Berkeley) 8/18/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __REC_GET -- Get a record from the btree. + * + * Parameters: + * dbp: pointer to access method + * key: key to find + * data: data to return + * flag: currently unused + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found. + */ +int +__rec_get(dbp, key, data, flags) + const DB *dbp; + const DBT *key; + DBT *data; + u_int flags; +{ + BTREE *t; + EPG *e; + recno_t nrec; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* Get currently doesn't take any flags, and keys of 0 are illegal. */ + if (flags || (nrec = *(recno_t *)key->data) == 0) { + errno = EINVAL; + return (RET_ERROR); + } + + /* + * If we haven't seen this record yet, try to find it in the + * original file. + */ + if (nrec > t->bt_nrecs) { + if (F_ISSET(t, R_EOF | R_INMEM)) + return (RET_SPECIAL); + if ((status = t->bt_irec(t, nrec)) != RET_SUCCESS) + return (status); + } + + --nrec; + if ((e = __rec_search(t, nrec, SEARCH)) == NULL) + return (RET_ERROR); + + status = __rec_ret(t, e, 0, NULL, data); + if (F_ISSET(t, B_DB_LOCK)) + mpool_put(t->bt_mp, e->page, 0); + else + t->bt_pinned = e->page; + return (status); +} + +/* + * __REC_FPIPE -- Get fixed length records from a pipe. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_fpipe(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + size_t len; + int ch; + u_char *p; + + if (t->bt_rdata.size < t->bt_reclen) { + t->bt_rdata.data = t->bt_rdata.data == NULL ? + malloc(t->bt_reclen) : + realloc(t->bt_rdata.data, t->bt_reclen); + if (t->bt_rdata.data == NULL) + return (RET_ERROR); + t->bt_rdata.size = t->bt_reclen; + } + data.data = t->bt_rdata.data; + data.size = t->bt_reclen; + + for (nrec = t->bt_nrecs; nrec < top;) { + len = t->bt_reclen; + for (p = t->bt_rdata.data;; *p++ = ch) + if ((ch = getc(t->bt_rfp)) == EOF || !--len) { + if (ch != EOF) + *p = ch; + if (len != 0) + memset(p, t->bt_bval, len); + if (__rec_iput(t, + nrec, &data, 0) != RET_SUCCESS) + return (RET_ERROR); + ++nrec; + break; + } + if (ch == EOF) + break; + } + if (nrec < top) { + F_SET(t, R_EOF); + return (RET_SPECIAL); + } + return (RET_SUCCESS); +} + +/* + * __REC_VPIPE -- Get variable length records from a pipe. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_vpipe(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + indx_t len; + size_t sz; + int bval, ch; + u_char *p; + + bval = t->bt_bval; + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + for (p = t->bt_rdata.data, + sz = t->bt_rdata.size;; *p++ = ch, --sz) { + if ((ch = getc(t->bt_rfp)) == EOF || ch == bval) { + data.data = t->bt_rdata.data; + data.size = p - (u_char *)t->bt_rdata.data; + if (ch == EOF && data.size == 0) + break; + if (__rec_iput(t, nrec, &data, 0) + != RET_SUCCESS) + return (RET_ERROR); + break; + } + if (sz == 0) { + len = p - (u_char *)t->bt_rdata.data; + t->bt_rdata.size += (sz = 256); + t->bt_rdata.data = t->bt_rdata.data == NULL ? + malloc(t->bt_rdata.size) : + realloc(t->bt_rdata.data, t->bt_rdata.size); + if (t->bt_rdata.data == NULL) + return (RET_ERROR); + p = (u_char *)t->bt_rdata.data + len; + } + } + if (ch == EOF) + break; + } + if (nrec < top) { + F_SET(t, R_EOF); + return (RET_SPECIAL); + } + return (RET_SUCCESS); +} + +/* + * __REC_FMAP -- Get fixed length records from a file. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_fmap(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + recno_t nrec; + u_char *sp, *ep, *p; + size_t len; + + if (t->bt_rdata.size < t->bt_reclen) { + t->bt_rdata.data = t->bt_rdata.data == NULL ? + malloc(t->bt_reclen) : + realloc(t->bt_rdata.data, t->bt_reclen); + if (t->bt_rdata.data == NULL) + return (RET_ERROR); + t->bt_rdata.size = t->bt_reclen; + } + data.data = t->bt_rdata.data; + data.size = t->bt_reclen; + + sp = (u_char *)t->bt_cmap; + ep = (u_char *)t->bt_emap; + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + if (sp >= ep) { + F_SET(t, R_EOF); + return (RET_SPECIAL); + } + len = t->bt_reclen; + for (p = t->bt_rdata.data; + sp < ep && len > 0; *p++ = *sp++, --len); + if (len != 0) + memset(p, t->bt_bval, len); + if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) + return (RET_ERROR); + } + t->bt_cmap = (caddr_t)sp; + return (RET_SUCCESS); +} + +/* + * __REC_VMAP -- Get variable length records from a file. + * + * Parameters: + * t: tree + * cnt: records to read + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_vmap(t, top) + BTREE *t; + recno_t top; +{ + DBT data; + u_char *sp, *ep; + recno_t nrec; + int bval; + + sp = (u_char *)t->bt_cmap; + ep = (u_char *)t->bt_emap; + bval = t->bt_bval; + + for (nrec = t->bt_nrecs; nrec < top; ++nrec) { + if (sp >= ep) { + F_SET(t, R_EOF); + return (RET_SPECIAL); + } + for (data.data = sp; sp < ep && *sp != bval; ++sp); + data.size = sp - (u_char *)data.data; + if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS) + return (RET_ERROR); + ++sp; + } + t->bt_cmap = (caddr_t)sp; + return (RET_SUCCESS); +} diff --git a/src/util/db2/recno/rec_open.c b/src/util/db2/recno/rec_open.c new file mode 100644 index 000000000..fccaacc90 --- /dev/null +++ b/src/util/db2/recno/rec_open.c @@ -0,0 +1,243 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_open.c 8.12 (Berkeley) 11/18/94"; +#endif /* LIBC_SCCS and not lint */ + +#include +#ifdef RECNO_USE_MMAP +#include +#endif +#include + +#include +#include +#include +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +DB * +__rec_open(fname, flags, mode, openinfo, dflags) + const char *fname; + int flags, mode, dflags; + const RECNOINFO *openinfo; +{ + BTREE *t; + BTREEINFO btopeninfo; + DB *dbp; + PAGE *h; + struct stat sb; + int rfd, sverrno; + + /* Open the user's file -- if this fails, we're done. */ + if (fname != NULL && (rfd = open(fname, flags, mode)) < 0) + return (NULL); + + /* Create a btree in memory (backed by disk). */ + dbp = NULL; + if (openinfo) { + if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT)) + goto einval; + btopeninfo.flags = 0; + btopeninfo.cachesize = openinfo->cachesize; + btopeninfo.maxkeypage = 0; + btopeninfo.minkeypage = 0; + btopeninfo.psize = openinfo->psize; + btopeninfo.compare = NULL; + btopeninfo.prefix = NULL; + btopeninfo.lorder = openinfo->lorder; + dbp = __bt_open(openinfo->bfname, + O_RDWR, S_IRUSR | S_IWUSR, &btopeninfo, dflags); + } else + dbp = __bt_open(NULL, O_RDWR, S_IRUSR | S_IWUSR, NULL, dflags); + if (dbp == NULL) + goto err; + + /* + * Some fields in the tree structure are recno specific. Fill them + * in and make the btree structure look like a recno structure. We + * don't change the bt_ovflsize value, it's close enough and slightly + * bigger. + */ + t = dbp->internal; + if (openinfo) { + if (openinfo->flags & R_FIXEDLEN) { + F_SET(t, R_FIXLEN); + t->bt_reclen = openinfo->reclen; + if (t->bt_reclen == 0) + goto einval; + } + t->bt_bval = openinfo->bval; + } else + t->bt_bval = '\n'; + + F_SET(t, R_RECNO); + if (fname == NULL) + F_SET(t, R_EOF | R_INMEM); + else + t->bt_rfd = rfd; + + if (fname != NULL) { + /* + * In 4.4BSD, stat(2) returns true for ISSOCK on pipes. + * Unfortunately, that's not portable, so we use lseek + * and check the errno values. + */ + errno = 0; + if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) { + switch (flags & O_ACCMODE) { + case O_RDONLY: + F_SET(t, R_RDONLY); + break; + default: + goto einval; + } +slow: if ((t->bt_rfp = fdopen(rfd, "r")) == NULL) + goto err; + F_SET(t, R_CLOSEFP); + t->bt_irec = + F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe; + } else { + switch (flags & O_ACCMODE) { + case O_RDONLY: + F_SET(t, R_RDONLY); + break; + case O_RDWR: + break; + default: + goto einval; + } + + if (fstat(rfd, &sb)) + goto err; + /* + * Kluge -- we'd like to test to see if the file is too + * big to mmap. Since, we don't know what size or type + * off_t's or size_t's are, what the largest unsigned + * integral type is, or what random insanity the local + * C compiler will perpetrate, doing the comparison in + * a portable way is flatly impossible. Hope that mmap + * fails if the file is too large. + */ + if (sb.st_size == 0) + F_SET(t, R_EOF); + else { +#ifdef RECNO_USE_MMAP + /* + * XXX + * Mmap doesn't work correctly on many current + * systems. In particular, it can fail subtly, + * with cache coherency problems. Don't use it + * for now. + */ + t->bt_msize = sb.st_size; + if ((t->bt_smap = mmap(NULL, t->bt_msize, + PROT_READ, MAP_PRIVATE, rfd, + (off_t)0)) == (caddr_t)-1) + goto slow; + t->bt_cmap = t->bt_smap; + t->bt_emap = t->bt_smap + sb.st_size; + t->bt_irec = F_ISSET(t, R_FIXLEN) ? + __rec_fmap : __rec_vmap; + F_SET(t, R_MEMMAPPED); +#else + goto slow; +#endif + } + } + } + + /* Use the recno routines. */ + dbp->close = __rec_close; + dbp->del = __rec_delete; + dbp->fd = __rec_fd; + dbp->get = __rec_get; + dbp->put = __rec_put; + dbp->seq = __rec_seq; + dbp->sync = __rec_sync; + + /* If the root page was created, reset the flags. */ + if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL) + goto err; + if ((h->flags & P_TYPE) == P_BLEAF) { + F_CLR(h, P_TYPE); + F_SET(h, P_RLEAF); + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } else + mpool_put(t->bt_mp, h, 0); + + if (openinfo && openinfo->flags & R_SNAPSHOT && + !F_ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + goto err; + return (dbp); + +einval: errno = EINVAL; +err: sverrno = errno; + if (dbp != NULL) + (void)__bt_close(dbp); + if (fname != NULL) + (void)close(rfd); + errno = sverrno; + return (NULL); +} + +int +__rec_fd(dbp) + const DB *dbp; +{ + BTREE *t; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* In-memory database can't have a file descriptor. */ + if (F_ISSET(t, R_INMEM)) { + errno = ENOENT; + return (-1); + } + return (t->bt_rfd); +} diff --git a/src/util/db2/recno/rec_put.c b/src/util/db2/recno/rec_put.c new file mode 100644 index 000000000..25452f15f --- /dev/null +++ b/src/util/db2/recno/rec_put.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_put.c 8.7 (Berkeley) 8/18/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __REC_PUT -- Add a recno item to the tree. + * + * Parameters: + * dbp: pointer to access method + * key: key + * data: data + * flag: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE + * + * Returns: + * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is + * already in the tree and R_NOOVERWRITE specified. + */ +int +__rec_put(dbp, key, data, flags) + const DB *dbp; + DBT *key; + const DBT *data; + u_int flags; +{ + BTREE *t; + DBT fdata, tdata; + recno_t nrec; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + /* + * If using fixed-length records, and the record is long, return + * EINVAL. If it's short, pad it out. Use the record data return + * memory, it's only short-term. + */ + if (F_ISSET(t, R_FIXLEN) && data->size != t->bt_reclen) { + if (data->size > t->bt_reclen) + goto einval; + + if (t->bt_rdata.size < t->bt_reclen) { + t->bt_rdata.data = t->bt_rdata.data == NULL ? + malloc(t->bt_reclen) : + realloc(t->bt_rdata.data, t->bt_reclen); + if (t->bt_rdata.data == NULL) + return (RET_ERROR); + t->bt_rdata.size = t->bt_reclen; + } + memmove(t->bt_rdata.data, data->data, data->size); + memset((char *)t->bt_rdata.data + data->size, + t->bt_bval, t->bt_reclen - data->size); + fdata.data = t->bt_rdata.data; + fdata.size = t->bt_reclen; + } else { + fdata.data = data->data; + fdata.size = data->size; + } + + switch (flags) { + case R_CURSOR: + if (!F_ISSET(&t->bt_cursor, CURS_INIT)) + goto einval; + nrec = t->bt_cursor.rcursor; + break; + case R_SETCURSOR: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_IAFTER: + if ((nrec = *(recno_t *)key->data) == 0) { + nrec = 1; + flags = R_IBEFORE; + } + break; + case 0: + case R_IBEFORE: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_NOOVERWRITE: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + if (nrec <= t->bt_nrecs) + return (RET_SPECIAL); + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + /* + * Make sure that records up to and including the put record are + * already in the database. If skipping records, create empty ones. + */ + if (nrec > t->bt_nrecs) { + if (!F_ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, nrec) == RET_ERROR) + return (RET_ERROR); + if (nrec > t->bt_nrecs + 1) { + if (F_ISSET(t, R_FIXLEN)) { + if ((tdata.data = + (void *)malloc(t->bt_reclen)) == NULL) + return (RET_ERROR); + tdata.size = t->bt_reclen; + memset(tdata.data, t->bt_bval, tdata.size); + } else { + tdata.data = NULL; + tdata.size = 0; + } + while (nrec > t->bt_nrecs + 1) + if (__rec_iput(t, + t->bt_nrecs, &tdata, 0) != RET_SUCCESS) + return (RET_ERROR); + if (F_ISSET(t, R_FIXLEN)) + free(tdata.data); + } + } + + if ((status = __rec_iput(t, nrec - 1, &fdata, flags)) != RET_SUCCESS) + return (status); + + if (flags == R_SETCURSOR) + t->bt_cursor.rcursor = nrec; + + F_SET(t, R_MODIFIED); + return (__rec_ret(t, NULL, nrec, key, NULL)); +} + +/* + * __REC_IPUT -- Add a recno item to the tree. + * + * Parameters: + * t: tree + * nrec: record number + * data: data + * + * Returns: + * RET_ERROR, RET_SUCCESS + */ +int +__rec_iput(t, nrec, data, flags) + BTREE *t; + recno_t nrec; + const DBT *data; + u_int flags; +{ + DBT tdata; + EPG *e; + PAGE *h; + indx_t index, nxtindex; + db_pgno_t pg; + u_int32_t nbytes; + int dflags, status; + char *dest, db[NOVFLSIZE]; + + /* + * If the data won't fit on a page, store it on indirect pages. + * + * XXX + * If the insert fails later on, these pages aren't recovered. + */ + if (data->size > t->bt_ovflsize) { + if (__ovfl_put(t, data, &pg) == RET_ERROR) + return (RET_ERROR); + tdata.data = db; + tdata.size = NOVFLSIZE; + *(db_pgno_t *)db = pg; + *(u_int32_t *)(db + sizeof(db_pgno_t)) = data->size; + dflags = P_BIGDATA; + data = &tdata; + } else + dflags = 0; + + /* __rec_search pins the returned page. */ + if ((e = __rec_search(t, nrec, + nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ? + SINSERT : SEARCH)) == NULL) + return (RET_ERROR); + + h = e->page; + index = e->index; + + /* + * Add the specified key/data pair to the tree. The R_IAFTER and + * R_IBEFORE flags insert the key after/before the specified key. + * + * Pages are split as required. + */ + switch (flags) { + case R_IAFTER: + ++index; + break; + case R_IBEFORE: + break; + default: + if (nrec < t->bt_nrecs && + __rec_dleaf(t, h, index) == RET_ERROR) { + mpool_put(t->bt_mp, h, 0); + return (RET_ERROR); + } + break; + } + + /* + * If not enough room, split the page. The split code will insert + * the key and data and unpin the current page. If inserting into + * the offset array, shift the pointers up. + */ + nbytes = NRLEAFDBT(data->size); + if (h->upper - h->lower < nbytes + sizeof(indx_t)) { + status = __bt_split(t, h, NULL, data, dflags, nbytes, index); + if (status == RET_SUCCESS) + ++t->bt_nrecs; + return (status); + } + + if (index < (nxtindex = NEXTINDEX(h))) + memmove(h->linp + index + 1, h->linp + index, + (nxtindex - index) * sizeof(indx_t)); + h->lower += sizeof(indx_t); + + h->linp[index] = h->upper -= nbytes; + dest = (char *)h + h->upper; + WR_RLEAF(dest, data, dflags); + + ++t->bt_nrecs; + F_SET(t, B_MODIFIED); + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + + return (RET_SUCCESS); +} diff --git a/src/util/db2/recno/rec_search.c b/src/util/db2/recno/rec_search.c new file mode 100644 index 000000000..0be9563b8 --- /dev/null +++ b/src/util/db2/recno/rec_search.c @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_search.c 8.4 (Berkeley) 7/14/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __REC_SEARCH -- Search a btree for a key. + * + * Parameters: + * t: tree to search + * recno: key to find + * op: search operation + * + * Returns: + * EPG for matching record, if any, or the EPG for the location of the + * key, if it were inserted into the tree. + * + * Returns: + * The EPG for matching record, if any, or the EPG for the location + * of the key, if it were inserted into the tree, is entered into + * the bt_cur field of the tree. A pointer to the field is returned. + */ +EPG * +__rec_search(t, recno, op) + BTREE *t; + recno_t recno; + enum SRCHOP op; +{ + register indx_t index; + register PAGE *h; + EPGNO *parent; + RINTERNAL *r; + db_pgno_t pg; + indx_t top; + recno_t total; + int sverrno; + + BT_CLR(t); + for (pg = P_ROOT, total = 0;;) { + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) + goto err; + if (h->flags & P_RLEAF) { + t->bt_cur.page = h; + t->bt_cur.index = recno - total; + return (&t->bt_cur); + } + for (index = 0, top = NEXTINDEX(h);;) { + r = GETRINTERNAL(h, index); + if (++index == top || total + r->nrecs > recno) + break; + total += r->nrecs; + } + + BT_PUSH(t, pg, index - 1); + + pg = r->pgno; + switch (op) { + case SDELETE: + --GETRINTERNAL(h, (index - 1))->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + case SINSERT: + ++GETRINTERNAL(h, (index - 1))->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + break; + case SEARCH: + mpool_put(t->bt_mp, h, 0); + break; + } + + } + /* Try and recover the tree. */ +err: sverrno = errno; + if (op != SEARCH) + while ((parent = BT_POP(t)) != NULL) { + if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL) + break; + if (op == SINSERT) + --GETRINTERNAL(h, parent->index)->nrecs; + else + ++GETRINTERNAL(h, parent->index)->nrecs; + mpool_put(t->bt_mp, h, MPOOL_DIRTY); + } + errno = sverrno; + return (NULL); +} diff --git a/src/util/db2/recno/rec_seq.c b/src/util/db2/recno/rec_seq.c new file mode 100644 index 000000000..e150333cb --- /dev/null +++ b/src/util/db2/recno/rec_seq.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)rec_seq.c 8.3 (Berkeley) 7/14/94"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __REC_SEQ -- Recno sequential scan interface. + * + * Parameters: + * dbp: pointer to access method + * key: key for positioning and return value + * data: data return value + * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV. + * + * Returns: + * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key. + */ +int +__rec_seq(dbp, key, data, flags) + const DB *dbp; + DBT *key, *data; + u_int flags; +{ + BTREE *t; + EPG *e; + recno_t nrec; + int status; + + t = dbp->internal; + + /* Toss any page pinned across calls. */ + if (t->bt_pinned != NULL) { + mpool_put(t->bt_mp, t->bt_pinned, 0); + t->bt_pinned = NULL; + } + + switch(flags) { + case R_CURSOR: + if ((nrec = *(recno_t *)key->data) == 0) + goto einval; + break; + case R_NEXT: + if (F_ISSET(&t->bt_cursor, CURS_INIT)) { + nrec = t->bt_cursor.rcursor + 1; + break; + } + /* FALLTHROUGH */ + case R_FIRST: + nrec = 1; + break; + case R_PREV: + if (F_ISSET(&t->bt_cursor, CURS_INIT)) { + if ((nrec = t->bt_cursor.rcursor - 1) == 0) + return (RET_SPECIAL); + break; + } + /* FALLTHROUGH */ + case R_LAST: + if (!F_ISSET(t, R_EOF | R_INMEM) && + t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR) + return (RET_ERROR); + nrec = t->bt_nrecs; + break; + default: +einval: errno = EINVAL; + return (RET_ERROR); + } + + if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) { + if (!F_ISSET(t, R_EOF | R_INMEM) && + (status = t->bt_irec(t, nrec)) != RET_SUCCESS) + return (status); + if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) + return (RET_SPECIAL); + } + + if ((e = __rec_search(t, nrec - 1, SEARCH)) == NULL) + return (RET_ERROR); + + F_SET(&t->bt_cursor, CURS_INIT); + t->bt_cursor.rcursor = nrec; + + status = __rec_ret(t, e, nrec, key, data); + if (F_ISSET(t, B_DB_LOCK)) + mpool_put(t->bt_mp, e->page, 0); + else + t->bt_pinned = e->page; + return (status); +} diff --git a/src/util/db2/recno/rec_utils.c b/src/util/db2/recno/rec_utils.c new file mode 100644 index 000000000..f757a724f --- /dev/null +++ b/src/util/db2/recno/rec_utils.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rec_utils.c 8.6 (Berkeley) 7/16/94"; +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include + +#include "db-int.h" +#include "recno.h" + +/* + * __rec_ret -- + * Build return data. + * + * Parameters: + * t: tree + * e: key/data pair to be returned + * nrec: record number + * key: user's key structure + * data: user's data structure + * + * Returns: + * RET_SUCCESS, RET_ERROR. + */ +int +__rec_ret(t, e, nrec, key, data) + BTREE *t; + EPG *e; + recno_t nrec; + DBT *key, *data; +{ + RLEAF *rl; + void *p; + + if (key == NULL) + goto dataonly; + + /* We have to copy the key, it's not on the page. */ + if (sizeof(recno_t) > t->bt_rkey.size) { + p = (void *)(t->bt_rkey.data == NULL ? + malloc(sizeof(recno_t)) : + realloc(t->bt_rkey.data, sizeof(recno_t))); + if (p == NULL) + return (RET_ERROR); + t->bt_rkey.data = p; + t->bt_rkey.size = sizeof(recno_t); + } + memmove(t->bt_rkey.data, &nrec, sizeof(recno_t)); + key->size = sizeof(recno_t); + key->data = t->bt_rkey.data; + +dataonly: + if (data == NULL) + return (RET_SUCCESS); + + /* + * We must copy big keys/data to make them contigous. Otherwise, + * leave the page pinned and don't copy unless the user specified + * concurrent access. + */ + rl = GETRLEAF(e->page, e->index); + if (rl->flags & P_BIGDATA) { + if (__ovfl_get(t, rl->bytes, + &data->size, &t->bt_rdata.data, &t->bt_rdata.size)) + return (RET_ERROR); + data->data = t->bt_rdata.data; + } else if (F_ISSET(t, B_DB_LOCK)) { + /* Use +1 in case the first record retrieved is 0 length. */ + if (rl->dsize + 1 > t->bt_rdata.size) { + p = (void *)(t->bt_rdata.data == NULL ? + malloc(rl->dsize + 1) : + realloc(t->bt_rdata.data, rl->dsize + 1)); + if (p == NULL) + return (RET_ERROR); + t->bt_rdata.data = p; + t->bt_rdata.size = rl->dsize + 1; + } + memmove(t->bt_rdata.data, rl->bytes, rl->dsize); + data->size = rl->dsize; + data->data = t->bt_rdata.data; + } else { + data->size = rl->dsize; + data->data = rl->bytes; + } + return (RET_SUCCESS); +} diff --git a/src/util/db2/recno/recno.h b/src/util/db2/recno/recno.h new file mode 100644 index 000000000..bec772c2f --- /dev/null +++ b/src/util/db2/recno/recno.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)recno.h 8.1 (Berkeley) 6/4/93 + */ + +enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */ + +#include "../btree/btree.h" +#include "extern.h" diff --git a/src/util/db2/test/Makefile b/src/util/db2/test/Makefile new file mode 100644 index 000000000..a5dd08ae5 --- /dev/null +++ b/src/util/db2/test/Makefile @@ -0,0 +1,23 @@ +# @(#)Makefile 8.15 (Berkeley) 7/28/94 + +PROG= dbtest +OBJS= dbtest.o strerror.o + +# Uncomment the STAT line get hash and btree statistical use info. This +# also forces ld to load the btree debug functions for use by gdb, which +# is useful. The db library has to be compiled with -DSTATISTICS as well. +INC= -I${PORTDIR}/include -I${PORTDIR} +OORG= -g +#STAT= -DSTATISTICS +CFLAGS= -D__DBINTERFACE_PRIVATE -DDEBUG ${STAT} ${OORG} ${INC} + +dbtest: ${OBJS} ${PORTDIR}/libdb.a + ${CC} -o $@ ${OBJS} ${PORTDIR}/libdb.a + +strerror.o: ${PORTDIR}/clib/strerror.c + ${CC} -c ${PORTDIR}/clib/strerror.c + +clean: + rm -f dbtest.core gmon.out ${OBJS} ${PROG} t1 t2 t3 + +${OBJS}: Makefile diff --git a/src/util/db2/test/README b/src/util/db2/test/README new file mode 100644 index 000000000..0c0cd13d8 --- /dev/null +++ b/src/util/db2/test/README @@ -0,0 +1,74 @@ +# @(#)README 8.8 (Berkeley) 7/31/94 + +To build this portably, try something like: + + make PORTDIR="../PORT/MACH" + +where MACH is the machine, i.e. "sunos.4.1.1". + +To run the tests, enter "sh run.test". If your system dictionary isn't +in /usr/share/dict/words, edit run.test to reflect the correct place. + +Fairly large files (the command files) are built in this directory during +the test runs, and even larger files (the database files) are created in +"/var/tmp". If the latter directory doesn't exist, set the environmental +variable TMPDIR to a directory where the files can be built. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +The script file consists of lines with an initial character which is +the command for that line, or an initial character indicating a key +or data entry for a previous command. + +Legal command characters are as follows: + +c: compare a record + + must be followed by [kK][dD]; the data value in the database + associated with the specified key is compared to the specified + data value. +e: echo a string + + writes out the rest of the line into the output file; if the + last character is not a carriage-return, a newline is appended. +f: set the flags for the next command + + no value zero's the flags +g: do a get command + + must be followed by [kK] + + writes out the retrieved data DBT. +o [r]: dump [reverse] + + dump the database out, if 'r' is set, in reverse order. +p: do a put command + + must be followed by [kK][dD] +r: do a del command + + must be followed by [kK] unless R_CURSOR flag set. +S: sync the database +s: do a seq command + + must be followed by [kK] if R_CURSOR flag set. + + writes out the retrieved data DBT. + +Legal key/data characters are as follows: + +D [file]: data file + + set the current data value to the contents of the file +d [data]: + + set the current key value to the contents of the line. +K [file]: key file + + set the current key value to the contents of the file +k [data]: + + set the current key value to the contents of the line. + +Blank lines, lines with leading white space, and lines with leading +hash marks (#) are ignored. + +Options to dbtest are as follows: + + -d: Set the DB_LOCK flag. + -f: Use the file argument as the database file. + -i: Use the rest of the argument to set elements in the info + structure. If the type is btree, then "-i cachesize=10240" + will set BTREEINFO.cachesize to 10240. + -o: The rest of the argument is the output file instead of + using stdout. + -s: Don't delete the database file before opening it, i.e. + use the database file from a previous run. + +Dbtest requires two arguments, the type of access "hash", "recno" +or "btree", and the script name or "-" to indicate stdin. diff --git a/src/util/db2/test/SEQ_TEST/data b/src/util/db2/test/SEQ_TEST/data new file mode 100644 index 000000000..37a518537 --- /dev/null +++ b/src/util/db2/test/SEQ_TEST/data @@ -0,0 +1,8 @@ +A000027875A000135891 +A000059165A000130168 +A000060256A000133490 +A040025906A000136770 +A040027881A000135829 +A040028611A000137873 +A040032413A000056974 +A040050163A000126233 diff --git a/src/util/db2/test/SEQ_TEST/mbox b/src/util/db2/test/SEQ_TEST/mbox new file mode 100644 index 000000000..9d5d49d07 --- /dev/null +++ b/src/util/db2/test/SEQ_TEST/mbox @@ -0,0 +1,399 @@ +From wiggans@aipl.arsusda.gov Mon Sep 12 11:05:58 1994 +Received: from vangogh.CS.Berkeley.EDU by python.bostic.com (8.6.9.Beta4/2.6) + id OAA16853; Mon, 12 Sep 1994 14:05:42 -0400 +From: wiggans@aipl.arsusda.gov +Received: from hofmann.CS.Berkeley.EDU (hofmann.CS.Berkeley.EDU [128.32.34.35]) by vangogh.CS.Berkeley.EDU (8.7.Alpha.1/8.6.9.Beta0) with ESMTP id LAA15825 for ; Mon, 12 Sep 1994 11:05:20 -0700 (PDT) +Received: from uu7.psi.com (uu7.psi.com [38.145.204.6]) by hofmann.CS.Berkeley.EDU (8.6.9/8.6.6.Beta11) with SMTP id LAA25681 for ; Mon, 12 Sep 1994 11:05:44 -0700 +Received: from AIPL.ARSUSDA.GOV by uu7.psi.com (5.65b/4.0.071791-PSI/PSINet) via SMTP; + id AA00699 for bostic@cs.berkeley.edu; Mon, 12 Sep 94 14:06:15 -0400 +Received: by aipl.arsusda.gov (AIX 3.2/UCB 5.64/4.03) + id AA14802; Mon, 12 Sep 1994 14:05:48 -0400 +Message-Id: <9409121805.AA14802@aipl.arsusda.gov> +Subject: db 1.85 problem +To: bostic@cs.berkeley.edu (Keith Bostic) +Date: Mon, 12 Sep 1994 14:05:47 -0400 (EDT) +X-Mailer: ELM [version 2.4 PL22] +Content-Type: text +Content-Length: 2553 +Status: RO + +In using the btree option to sequentially read and then write a file, we +are having a problem with 1.85. When compiled with 1.73 there is no +problem. The problem is that the seq call keeps reading the same record. +The code follows: + +/* chkseq.c Check sequential read and write */ + +#include +#include +#include +#include +#include /* O_CREAT, O_RDWR */ +#include /* Error numbers */ +#include + +extern int errno; +extern char *sys_errlist[]; + +typedef struct idst { + char id1[7]; +} id; + +void cvtid(char *, char *); + +void main() { + char anim10[11], datastor[212],keystor[10], *pc; + int i; + long in = 0L; + DB *dbp, *dbpo; + DBT key, data, keyo, datao; + + if ((dbp = dbopen("bullxrf.db", O_RDWR, 0664 + , DB_BTREE, NULL )) == NULL) { + printf("\n Error on dbopen %d %s\n",errno,strerror(errno)); + exit(61); + } + key.size = 7; + keyo.size = 7; + + while (dbp->seq(dbp, &key, &data,R_NEXT) == 0) { + in++; + if (in > 20) break; +/* pc = (char *) key.data; +for (i=0;iput(dbp, &keyo, &datao,0) != 0) { + printf("Write failed at %d\n",in); + exit(85); + } +/* } + */ + } + printf("%d Records copied\n",in); + dbp->close(dbp); +} + +I am running on an RS/6000 AIX 3.2.5. The section of the make file +follows: + +# Make file +all: chkseq + +chkseq: chkseq.c + cc -gO3 -lm -o chkseq\ + -L /data6/hash/include/sys/lib -l db -I /data6/hash/include \ + chkseq.c cvtid.o ascii.o +# -L /data12/db.1.85 -l db -I /data12/db.1.85/include \ + +We would appreciate your help. +Thanks, + +-- +George Wiggans I================================================I + |Animal Improvement Programs Laboratory | +Phone: 301-504-8407 |Bldg 263 Beltsville Agricultural Research Center| +FAX: 301-504-8092 |Beltsville, MD 20705-2350 USA | +wiggans@aipl.arsusda.gov | | +=========================I================================================I + +From wiggans@aipl.arsusda.gov Fri Sep 16 20:27:22 1994 +Received: from vangogh.CS.Berkeley.EDU by python.bostic.com (8.6.9.Beta4/2.6) + id XAA09260; Fri, 16 Sep 1994 23:27:09 -0400 +From: wiggans@aipl.arsusda.gov +Received: from hofmann.CS.Berkeley.EDU (hofmann.CS.Berkeley.EDU [128.32.34.35]) by vangogh.CS.Berkeley.EDU (8.7.Alpha.1/8.6.9.Beta0) with ESMTP id UAA25674 for ; Fri, 16 Sep 1994 20:27:03 -0700 (PDT) +Received: from uu7.psi.com (uu7.psi.com [38.145.204.6]) by hofmann.CS.Berkeley.EDU (8.6.9/8.6.6.Beta11) with SMTP id UAA15043 for ; Fri, 16 Sep 1994 20:27:16 -0700 +Received: from AIPL.ARSUSDA.GOV by uu7.psi.com (5.65b/4.0.071791-PSI/PSINet) via SMTP; + id AA18737 for bostic@cs.berkeley.edu; Fri, 16 Sep 94 23:27:14 -0400 +Received: by aipl.arsusda.gov (AIX 3.2/UCB 5.64/4.03) + id AA10907; Fri, 16 Sep 1994 23:26:18 -0400 +Message-Id: <9409170326.AA10907@aipl.arsusda.gov> +Subject: Test case +To: bostic@cs.berkeley.edu (Keith Bostic) +Date: Fri, 16 Sep 1994 23:26:16 -0400 (EDT) +X-Mailer: ELM [version 2.4 PL22] +Content-Type: text +Content-Length: 3713 +Status: RO + +The following program loads 2 10 character animal ID which are used to +change an animal's ID. After loading, it closes, then opens and +sequentially reads and rewrites the file changing the first character of +the 2nd ID to U. Failure is observed when the update part gets stuck on +the first record, rereading it. The last step displays the updated file. +The name of the data file is a command line argument. + +The data: +A000027875A000135891 +A000059165A000130168 +A000060256A000133490 +A040025906A000136770 +A040027881A000135829 +A040028611A000137873 +A040032413A000056974 +A040050163A000126233 +A040050329A000126177 +A040050411A000119017 +A040050995A000116767 +A040051022A000126669 +A040051276A000127444 +A040051514A000120563 +A040051597A000127287 +A040051627A000127284 +A040051700A000126914 +A040051810A000127286 +A040051964A000118834 +A040052164A000135104 +A040052165A000127688 +A040052186A000126926 +A040052530A000126287 +A040052560A000119160 +A040052892A000125334 +A040053004A000127684 +A040053359A000128628 +A040053378A000137680 +A040053416A000128825 +A040053589A000120369 +A040053620A000128460 +A040053751A000123525 +A040053754A000126736 +A040054191A000126286 +A040054251A000121745 +A040054253A000127848 +A040054596A000130931 +A040054981A000128731 +A040055000A000127689 + +The program: +/* chkseq.c Check sequential read and write */ + +#include +#include +#include +#include +#include /* O_CREAT, O_RDWR */ +#include /* Error numbers */ +#include + +extern int errno; +extern char *sys_errlist[]; + + +void main(int argc, char *argv[]) { + char id1[] = {" "}, id2[] = {" "}; + int i; + long in = 0L, out = 0L; + DB *dbp, *dbpo; + DBT key, data, keyo, datao; + FILE *fopen(), *fin; + + if ((fin = fopen(argv[1],"r")) == NULL) { + printf("Unable to open %s\n",argv[1]); + exit(25); + } + if ((dbp = dbopen("test.db",O_RDWR | O_CREAT, 0664 + , DB_BTREE, NULL )) == NULL) { + printf("\n Open error on test.db %d %s\n",errno,strerror(errno)); + exit(25); + } + + while (fscanf(fin," %10s%10s",id1,id2) > 0) { + key.size = 11; + data.size = 11; + key.data = id1; + data.data = id2; + printf("%10s %10s\n",key.data,data.data); + if (dbp->put(dbp, &key, &data,R_NOOVERWRITE) != 0) { + printf("Error writing output\n"); + } + out++; + } + printf("%d Records in\n",out); + dbp->close(dbp); + + if ((dbp = dbopen("test.db", O_RDWR, 0664 + , DB_BTREE, NULL )) == NULL) { + printf("\n Error on dbopen %d %s\n",errno,strerror(errno)); + exit(61); + } + + while (dbp->seq(dbp, &key, &data,R_NEXT) == 0) { + strcpy(id1,key.data); + keyo.size = 11; + datao.size = 11; + keyo.data = id1; + strcpy(id2,data.data); + id2[0] = 'U'; + datao.data=id2; + printf("%10s %10s\n",key.data,data.data); + in++; + if (in > 50) break; + if (dbp->put(dbp, &keyo, &datao,0) != 0) { + printf("Write failed at %d\n",in); + exit(85); + } + } + printf("%d Records copied\n",in); + in = 0; + dbp->seq(dbp, &key, &data,R_FIRST); + printf("%10s %10s\n",key.data,data.data); + in++; + while (dbp->seq(dbp, &key, &data,R_NEXT) == 0) { + in++; + printf("%10s %10s\n",key.data,data.data); + } + printf("%d Records read\n",in); + dbp->close(dbp); +} + + +-- +George Wiggans I================================================I + |Animal Improvement Programs Laboratory | +Phone: 301-504-8407 |Bldg 263 Beltsville Agricultural Research Center| +FAX: 301-504-8092 |Beltsville, MD 20705-2350 USA | +wiggans@aipl.arsusda.gov | | +=========================I================================================I + +From bostic Fri Sep 23 08:44:56 1994 +To: wiggans@aipl.arsusda.gov /usr/src/local/db/test/SEQ_TEST/mbox +Subject: Re: Test case + + +OK, I've attached a tentative patch for the bug, that appears +to fix it on my local system. Please let me know if you have +any further problems with this. + +There are a couple of issues here. The first, is that to some +extent, the btree code is correct. You aren't replacing the +cursor in your test program, you're adding a new key, which +just happens to be where the cursor was. So, the btree code +is doing you a favor by returning the new key as part of the +cursor walk, and it's not its fault. ;-} + +However, because a put to the cursor record is done using a +delete/add pair, doing it the "right" way will result in the +exact same behavior as you saw doing it "wrong". + +Thinking about this further, there's another bug that's going to +hit eventually -- if you have duplicate records, the current +scheme of doing delete/add to replace the cursor record can result +in a record being returned twice, which is tacky at best, if not +actually wrong. + +I think I may have to revisit how duplicate records are stored. +Which does not make me happy. ;-{ + +--keith + +*** db/btree/bt_seq.c.orig Fri Sep 23 08:35:06 1994 +--- db/btree/bt_seq.c Fri Sep 23 08:34:58 1994 +*************** +*** 35,41 **** + */ + + #if defined(LIBC_SCCS) && !defined(lint) +! static char sccsid[] = "@(#)bt_seq.c 8.7 (Berkeley) 7/20/94"; + #endif /* LIBC_SCCS and not lint */ + + #include +--- 35,41 ---- + */ + + #if defined(LIBC_SCCS) && !defined(lint) +! static char sccsid[] = "@(#)bt_seq.c 8.8 (Berkeley) 9/23/94"; + #endif /* LIBC_SCCS and not lint */ + + #include +*************** +*** 246,252 **** + PAGE *h; + indx_t index; + pgno_t pg; +! int exact; + + /* + * There are a couple of states that we can be in. The cursor has +--- 246,252 ---- + PAGE *h; + indx_t index; + pgno_t pg; +! int exact, rval; + + /* + * There are a couple of states that we can be in. The cursor has +*************** +*** 255,269 **** + c = &t->bt_cursor; + + /* +! * The cursor was deleted where there weren't any duplicate records, +! * so the key was saved. Find out where that key would go in the +! * current tree. It doesn't matter if the returned key is an exact +! * match or not -- if it's an exact match, the record was added after +! * the delete so we can just return it. If not, as long as there's +! * a record there, return it. + */ +! if (F_ISSET(c, CURS_ACQUIRE)) +! return (__bt_first(t, &c->key, ep, &exact)); + + /* Get the page referenced by the cursor. */ + if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) +--- 255,299 ---- + c = &t->bt_cursor; + + /* +! * The cursor was deleted and there weren't any duplicate records, +! * so the cursor's key was saved. Find out where that key would +! * be in the current tree. If the returned key is an exact match, +! * it means that a key/data pair was inserted into the tree after +! * the delete. We could reasonably return the key, but the problem +! * is that this is the access pattern we'll see if the user is +! * doing seq(..., R_NEXT)/put(..., 0) pairs, i.e. the put deletes +! * the cursor record and then replaces it, so the cursor was saved, +! * and we'll simply return the same "new" record until the user +! * notices and doesn't do a put() of it. Since the key is an exact +! * match, we could as easily put the new record before the cursor, +! * and we've made no guarantee to return it. So, move forward or +! * back a record if it's an exact match. +! * +! * XXX +! * In the current implementation, put's to the cursor are done with +! * delete/add pairs. This has two consequences. First, it means +! * that seq(..., R_NEXT)/put(..., R_CURSOR) pairs are going to exhibit +! * the same behavior as above. Second, you can return the same key +! * twice if you have duplicate records. The scenario is that the +! * cursor record is deleted, moving the cursor forward or backward +! * to a duplicate. The add then inserts the new record at a location +! * ahead of the cursor because duplicates aren't sorted in any way, +! * and the new record is later returned. This has to be fixed at some +! * point. + */ +! if (F_ISSET(c, CURS_ACQUIRE)) { +! if (rval = __bt_first(t, &c->key, ep, &exact)) +! return (RET_ERROR); +! if (!exact) +! return (rval); +! /* +! * XXX +! * Kluge -- get, release, get the page. +! */ +! c->pg.pgno = ep->page->pgno; +! c->pg.index = ep->index; +! mpool_put(t->bt_mp, ep->page, 0); +! } + + /* Get the page referenced by the cursor. */ + if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL) + + + diff --git a/src/util/db2/test/SEQ_TEST/t.c b/src/util/db2/test/SEQ_TEST/t.c new file mode 100644 index 000000000..ac0fef71e --- /dev/null +++ b/src/util/db2/test/SEQ_TEST/t.c @@ -0,0 +1,86 @@ +/* chkseq.c Check sequential read and write */ + +#include +#include "db-int.h" +#include /* Error numbers */ +#include /* O_CREAT, O_RDWR */ +#include +#include +#include + +extern int errno; + +void main(int argc, char *argv[]) { + char id1[] = {" "}, id2[] = {" "}; + int i; + long in = 0L, out = 0L; + DB *dbp, *dbpo; + DBT key, data, keyo, datao; + FILE *fopen(), *fin; + + unlink("test.db"); + if ((fin = fopen("data","r")) == NULL) { + printf("Unable to open %s\n","data"); + exit(25); + } + if ((dbp = dbopen("test.db",O_RDWR | O_CREAT, 0664 + , DB_BTREE, NULL )) == NULL) { + printf("\n Open error on test.db %d %s\n",errno,strerror(errno)); + exit(25); + } + + while (fscanf(fin," %10s%10s",id1,id2) > 0) { + key.size = 11; + data.size = 11; + key.data = id1; + data.data = id2; + printf("%10s %10s\n",key.data,data.data); + if (dbp->put(dbp, &key, &data,R_NOOVERWRITE) != 0) { + printf("Error writing output\n"); + } + out++; + } + printf("%d Records in\n",out); + dbp->close(dbp); + + if ((dbp = dbopen("test.db", O_RDWR, 0664 + , DB_BTREE, NULL )) == NULL) { + printf("\n Error on dbopen %d %s\n",errno,strerror(errno)); + exit(61); + } + + while (dbp->seq(dbp, &key, &data,R_NEXT) == 0) { + strcpy(id1,key.data); + keyo.size = 11; + datao.size = 11; + keyo.data = id1; + strcpy(id2,data.data); + id2[0] = 'U'; + datao.data=id2; + printf("%10s %10s\n",key.data,data.data); + in++; + if (in > 10) break; +#ifdef notdef + if (dbp->put(dbp, &keyo, &datao,0) != 0) { + printf("Write failed at %d\n",in); + exit(85); + } +#else + if (dbp->put(dbp, &keyo, &datao,R_CURSOR) != 0) { + printf("Write failed at %d\n",in); + exit(85); + } +#endif + } + printf("%d Records copied\n",in); + in = 0; + dbp->seq(dbp, &key, &data,R_FIRST); + printf("%10s %10s\n",key.data,data.data); + in++; + while (dbp->seq(dbp, &key, &data,R_NEXT) == 0) { + in++; + printf("%10s %10s\n",key.data,data.data); + } + printf("%d Records read\n",in); + dbp->close(dbp); +} diff --git a/src/util/db2/test/btree.tests/main.c b/src/util/db2/test/btree.tests/main.c new file mode 100644 index 000000000..02f1b9628 --- /dev/null +++ b/src/util/db2/test/btree.tests/main.c @@ -0,0 +1,765 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Olson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include "db-int.h" +#include +#include +#include +#include +#include +#include "btree.h" + +typedef struct cmd_table { + char *cmd; + int nargs; + int rconv; + void (*func) __P((DB *, char **)); + char *usage, *descrip; +} cmd_table; + +int stopstop; +DB *globaldb; + +void append __P((DB *, char **)); +void bstat __P((DB *, char **)); +void cursor __P((DB *, char **)); +void delcur __P((DB *, char **)); +void delete __P((DB *, char **)); +void dump __P((DB *, char **)); +void first __P((DB *, char **)); +void get __P((DB *, char **)); +void help __P((DB *, char **)); +void iafter __P((DB *, char **)); +void ibefore __P((DB *, char **)); +void icursor __P((DB *, char **)); +void insert __P((DB *, char **)); +void keydata __P((DBT *, DBT *)); +void last __P((DB *, char **)); +void list __P((DB *, char **)); +void load __P((DB *, char **)); +void mstat __P((DB *, char **)); +void next __P((DB *, char **)); +int parse __P((char *, char **, int)); +void previous __P((DB *, char **)); +void show __P((DB *, char **)); +void usage __P((void)); +void user __P((DB *)); + +cmd_table commands[] = { + "?", 0, 0, help, "help", NULL, + "a", 2, 1, append, "append key def", "append key with data def", + "b", 0, 0, bstat, "bstat", "stat btree", + "c", 1, 1, cursor, "cursor word", "move cursor to word", + "delc", 0, 0, delcur, "delcur", "delete key the cursor references", + "dele", 1, 1, delete, "delete word", "delete word", + "d", 0, 0, dump, "dump", "dump database", + "f", 0, 0, first, "first", "move cursor to first record", + "g", 1, 1, get, "get key", "locate key", + "h", 0, 0, help, "help", "print command summary", + "ia", 2, 1, iafter, "iafter key data", "insert data after key", + "ib", 2, 1, ibefore, "ibefore key data", "insert data before key", + "ic", 2, 1, icursor, "icursor key data", "replace cursor", + "in", 2, 1, insert, "insert key def", "insert key with data def", + "la", 0, 0, last, "last", "move cursor to last record", + "li", 1, 1, list, "list file", "list to a file", + "loa", 1, 0, load, "load file", NULL, + "loc", 1, 1, get, "get key", NULL, + "m", 0, 0, mstat, "mstat", "stat memory pool", + "n", 0, 0, next, "next", "move cursor forward one record", + "p", 0, 0, previous, "previous", "move cursor back one record", + "q", 0, 0, NULL, "quit", "quit", + "sh", 1, 0, show, "show page", "dump a page", + { NULL }, +}; + +int recno; /* use record numbers */ +char *dict = "words"; /* default dictionary */ +char *progname; + +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + DB *db; + BTREEINFO b; + + progname = *argv; + + b.flags = 0; + b.cachesize = 0; + b.maxkeypage = 0; + b.minkeypage = 0; + b.psize = 0; + b.compare = NULL; + b.prefix = NULL; + b.lorder = 0; + + while ((c = getopt(argc, argv, "bc:di:lp:ru")) != EOF) { + switch (c) { + case 'b': + b.lorder = BIG_ENDIAN; + break; + case 'c': + b.cachesize = atoi(optarg); + break; + case 'd': + b.flags |= R_DUP; + break; + case 'i': + dict = optarg; + break; + case 'l': + b.lorder = LITTLE_ENDIAN; + break; + case 'p': + b.psize = atoi(optarg); + break; + case 'r': + recno = 1; + break; + case 'u': + b.flags = 0; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (recno) + db = dbopen(*argv == NULL ? NULL : *argv, O_RDWR, + 0, DB_RECNO, NULL); + else + db = dbopen(*argv == NULL ? NULL : *argv, O_CREAT|O_RDWR, + 0600, DB_BTREE, &b); + + if (db == NULL) { + (void)fprintf(stderr, "dbopen: %s\n", strerror(errno)); + exit(1); + } + globaldb = db; + user(db); + exit(0); + /* NOTREACHED */ +} + +void +user(db) + DB *db; +{ + FILE *ifp; + int argc, i, last; + char *lbuf, *argv[4], buf[512]; + + if ((ifp = fopen("/dev/tty", "r")) == NULL) { + (void)fprintf(stderr, + "/dev/tty: %s\n", strerror(errno)); + exit(1); + } + for (last = 0;;) { + (void)printf("> "); + (void)fflush(stdout); + if ((lbuf = fgets(&buf[0], 512, ifp)) == NULL) + break; + if (lbuf[0] == '\n') { + i = last; + goto uselast; + } + lbuf[strlen(lbuf) - 1] = '\0'; + + if (lbuf[0] == 'q') + break; + + argc = parse(lbuf, &argv[0], 3); + if (argc == 0) + continue; + + for (i = 0; commands[i].cmd != NULL; i++) + if (strncmp(commands[i].cmd, argv[0], + strlen(commands[i].cmd)) == 0) + break; + + if (commands[i].cmd == NULL) { + (void)fprintf(stderr, + "%s: command unknown ('help' for help)\n", lbuf); + continue; + } + + if (commands[i].nargs != argc - 1) { + (void)fprintf(stderr, "usage: %s\n", commands[i].usage); + continue; + } + + if (recno && commands[i].rconv) { + static recno_t nlong; + nlong = atoi(argv[1]); + argv[1] = (char *)&nlong; + } +uselast: last = i; + (*commands[i].func)(db, argv); + } + if ((db->sync)(db) == RET_ERROR) + perror("dbsync"); + else if ((db->close)(db) == RET_ERROR) + perror("dbclose"); +} + +int +parse(lbuf, argv, maxargc) + char *lbuf, **argv; + int maxargc; +{ + int argc = 0; + char *c; + + c = lbuf; + while (isspace(*c)) + c++; + while (*c != '\0' && argc < maxargc) { + *argv++ = c; + argc++; + while (!isspace(*c) && *c != '\0') { + c++; + } + while (isspace(*c)) + *c++ = '\0'; + } + return (argc); +} + +void +append(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "append only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_APPEND); + switch (status) { + case RET_ERROR: + perror("append/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +cursor(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + status = (*db->seq)(db, &key, &data, R_CURSOR); + switch (status) { + case RET_ERROR: + perror("cursor/seq"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +delcur(db, argv) + DB *db; + char **argv; +{ + int status; + + status = (*db->del)(db, NULL, R_CURSOR); + + if (status == RET_ERROR) + perror("delcur/del"); +} + +void +delete(db, argv) + DB *db; + char **argv; +{ + DBT key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + + status = (*db->del)(db, &key, 0); + switch (status) { + case RET_ERROR: + perror("delete/del"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + break; + } +} + +void +dump(db, argv) + DB *db; + char **argv; +{ + __bt_dump(db); +} + +void +first(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_FIRST); + + switch (status) { + case RET_ERROR: + perror("first/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +get(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + + status = (*db->get)(db, &key, &data, 0); + + switch (status) { + case RET_ERROR: + perror("get/get"); + break; + case RET_SPECIAL: + (void)printf("key not found\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +help(db, argv) + DB *db; + char **argv; +{ + int i; + + for (i = 0; commands[i].cmd; i++) + if (commands[i].descrip) + (void)printf("%s: %s\n", + commands[i].usage, commands[i].descrip); +} + +void +iafter(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "iafter only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_IAFTER); + switch (status) { + case RET_ERROR: + perror("iafter/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +ibefore(db, argv) + DB *db; + char **argv; +{ + DBT key, data; + int status; + + if (!recno) { + (void)fprintf(stderr, + "ibefore only available for recno db's.\n"); + return; + } + key.data = argv[1]; + key.size = sizeof(recno_t); + data.data = argv[2]; + data.size = strlen(data.data); + status = (db->put)(db, &key, &data, R_IBEFORE); + switch (status) { + case RET_ERROR: + perror("ibefore/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +icursor(db, argv) + DB *db; + char **argv; +{ + int status; + DBT data, key; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + data.data = argv[2]; + data.size = strlen(argv[2]) + 1; + + status = (*db->put)(db, &key, &data, R_CURSOR); + switch (status) { + case RET_ERROR: + perror("icursor/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +insert(db, argv) + DB *db; + char **argv; +{ + int status; + DBT data, key; + + key.data = argv[1]; + if (recno) + key.size = sizeof(recno_t); + else + key.size = strlen(argv[1]) + 1; + data.data = argv[2]; + data.size = strlen(argv[2]) + 1; + + status = (*db->put)(db, &key, &data, R_NOOVERWRITE); + switch (status) { + case RET_ERROR: + perror("insert/put"); + break; + case RET_SPECIAL: + (void)printf("%s (duplicate key)\n", argv[1]); + break; + case RET_SUCCESS: + break; + } +} + +void +last(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_LAST); + + switch (status) { + case RET_ERROR: + perror("last/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +list(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + FILE *fp; + int status; + + if ((fp = fopen(argv[1], "w")) == NULL) { + (void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); + return; + } + status = (*db->seq)(db, &key, &data, R_FIRST); + while (status == RET_SUCCESS) { + (void)fprintf(fp, "%s\n", key.data); + status = (*db->seq)(db, &key, &data, R_NEXT); + } + if (status == RET_ERROR) + perror("list/seq"); +} + +DB *BUGdb; +void +load(db, argv) + DB *db; + char **argv; +{ + register char *p, *t; + FILE *fp; + DBT data, key; + recno_t cnt; + size_t len; + int status; + char *lp, buf[16 * 1024]; + + BUGdb = db; + if ((fp = fopen(argv[1], "r")) == NULL) { + (void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); + return; + } + (void)printf("loading %s...\n", argv[1]); + + for (cnt = 1; (lp = fgetline(fp, &len)) != NULL; ++cnt) { + if (recno) { + key.data = &cnt; + key.size = sizeof(recno_t); + data.data = lp; + data.size = len + 1; + } else { + key.data = lp; + key.size = len + 1; + for (p = lp + len - 1, t = buf; p >= lp; *t++ = *p--); + *t = '\0'; + data.data = buf; + data.size = len + 1; + } + + status = (*db->put)(db, &key, &data, R_NOOVERWRITE); + switch (status) { + case RET_ERROR: + perror("load/put"); + exit(1); + case RET_SPECIAL: + if (recno) + (void)fprintf(stderr, + "duplicate: %ld {%s}\n", cnt, data.data); + else + (void)fprintf(stderr, + "duplicate: %ld {%s}\n", cnt, key.data); + exit(1); + case RET_SUCCESS: + break; + } + } + (void)fclose(fp); +} + +void +next(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_NEXT); + + switch (status) { + case RET_ERROR: + perror("next/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +previous(db, argv) + DB *db; + char **argv; +{ + DBT data, key; + int status; + + status = (*db->seq)(db, &key, &data, R_PREV); + + switch (status) { + case RET_ERROR: + perror("previous/seq"); + break; + case RET_SPECIAL: + (void)printf("no more keys\n"); + break; + case RET_SUCCESS: + keydata(&key, &data); + break; + } +} + +void +show(db, argv) + DB *db; + char **argv; +{ + BTREE *t; + PAGE *h; + db_pgno_t pg; + + pg = atoi(argv[1]); + t = db->internal; + if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) { + (void)printf("getpage of %ld failed\n", pg); + return; + } + if (pg == 0) + __bt_dmpage(h); + else + __bt_dpage(h); + mpool_put(t->bt_mp, h, 0); +} + +void +bstat(db, argv) + DB *db; + char **argv; +{ + (void)printf("BTREE\n"); + __bt_stat(db); +} + +void +mstat(db, argv) + DB *db; + char **argv; +{ + (void)printf("MPOOL\n"); + mpool_stat(((BTREE *)db->internal)->bt_mp); +} + +void +keydata(key, data) + DBT *key, *data; +{ + if (!recno && key->size > 0) + (void)printf("%s/", key->data); + if (data->size > 0) + (void)printf("%s", data->data); + (void)printf("\n"); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s [-bdlu] [-c cache] [-i file] [-p page] [file]\n", + progname); + exit (1); +} diff --git a/src/util/db2/test/dbtest.c b/src/util/db2/test/dbtest.c new file mode 100644 index 000000000..f49228165 --- /dev/null +++ b/src/util/db2/test/dbtest.c @@ -0,0 +1,753 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "db-int.h" + +enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; + +void compare __P((DBT *, DBT *)); +DBTYPE dbtype __P((char *)); +void dump __P((DB *, int)); +void err __P((const char *, ...)); +void get __P((DB *, DBT *)); +void getdata __P((DB *, DBT *, DBT *)); +void put __P((DB *, DBT *, DBT *)); +void rem __P((DB *, DBT *)); +char *sflags __P((int)); +void synk __P((DB *)); +void *rfile __P((char *, size_t *)); +void seq __P((DB *, DBT *)); +u_int setflags __P((char *)); +void *setinfo __P((DBTYPE, char *)); +void usage __P((void)); +void *xmalloc __P((char *, size_t)); + +DBTYPE type; /* Database type. */ +void *infop; /* Iflags. */ +u_long lineno; /* Current line in test script. */ +u_int flags; /* Current DB flags. */ +int ofd = STDOUT_FILENO; /* Standard output fd. */ + +DB *XXdbp; /* Global for gdb. */ +int XXlineno; /* Fast breakpoint for gdb. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + enum S command, state; + DB *dbp; + DBT data, key, keydata; + size_t len; + int ch, oflags, sflag; + char *fname, *infoarg, *p, *t, buf[8 * 1024]; + + infoarg = NULL; + fname = NULL; + oflags = O_CREAT | O_RDWR; + sflag = 0; + while ((ch = getopt(argc, argv, "f:i:lo:s")) != EOF) + switch (ch) { + case 'f': + fname = optarg; + break; + case 'i': + infoarg = optarg; + break; + case 'l': + oflags |= DB_LOCK; + break; + case 'o': + if ((ofd = open(optarg, + O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + err("%s: %s", optarg, strerror(errno)); + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* Set the type. */ + type = dbtype(*argv++); + + /* Open the descriptor file. */ + if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) + err("%s: %s", *argv, strerror(errno)); + + /* Set up the db structure as necessary. */ + if (infoarg == NULL) + infop = NULL; + else + for (p = strtok(infoarg, ",\t "); p != NULL; + p = strtok(0, ",\t ")) + if (*p != '\0') + infop = setinfo(type, p); + + /* + * Open the DB. Delete any preexisting copy, you almost never + * want it around, and it often screws up tests. + */ + if (fname == NULL) { + p = getenv("TMPDIR"); + if (p == NULL) + p = "/var/tmp"; + (void)sprintf(buf, "%s/__dbtest", p); + fname = buf; + (void)unlink(buf); + } else if (!sflag) + (void)unlink(fname); + + if ((dbp = dbopen(fname, + oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) + err("dbopen: %s", strerror(errno)); + XXdbp = dbp; + + state = COMMAND; + for (lineno = 1; + (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { + /* Delete the newline, displaying the key/data is easier. */ + if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) + *t = '\0'; + if ((len = strlen(buf)) == 0 || isspace(*p) || *p == '#') + continue; + + /* Convenient gdb break point. */ + if (XXlineno == lineno) + XXlineno = 1; + switch (*p) { + case 'c': /* compare */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = COMPARE; + break; + case 'e': /* echo */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + /* Don't display the newline, if CR at EOL. */ + if (p[len - 2] == '\r') + --len; + if (write(ofd, p + 1, len - 1) != len - 1 || + write(ofd, "\n", 1) != 1) + err("write: %s", strerror(errno)); + break; + case 'g': /* get */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = GET; + break; + case 'p': /* put */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + state = KEY; + command = PUT; + break; + case 'r': /* remove */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + if (flags == R_CURSOR) { + rem(dbp, &key); + state = COMMAND; + } else { + state = KEY; + command = REMOVE; + } + break; + case 'S': /* sync */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + synk(dbp); + state = COMMAND; + break; + case 's': /* seq */ + if (state != COMMAND) + err("line %lu: not expecting command", lineno); + if (flags == R_CURSOR) { + state = KEY; + command = SEQ; + } else + seq(dbp, &key); + break; + case 'f': + flags = setflags(p + 1); + break; + case 'D': /* data file */ + if (state != DATA) + err("line %lu: not expecting data", lineno); + data.data = rfile(p + 1, &data.size); + goto ldata; + case 'd': /* data */ + if (state != DATA) + err("line %lu: not expecting data", lineno); + data.data = xmalloc(p + 1, len - 1); + data.size = len - 1; +ldata: switch (command) { + case COMPARE: + compare(&keydata, &data); + break; + case PUT: + put(dbp, &key, &data); + break; + default: + err("line %lu: command doesn't take data", + lineno); + } + if (type != DB_RECNO) + free(key.data); + free(data.data); + state = COMMAND; + break; + case 'K': /* key file */ + if (state != KEY) + err("line %lu: not expecting a key", lineno); + if (type == DB_RECNO) + err("line %lu: 'K' not available for recno", + lineno); + key.data = rfile(p + 1, &key.size); + goto lkey; + case 'k': /* key */ + if (state != KEY) + err("line %lu: not expecting a key", lineno); + if (type == DB_RECNO) { + static recno_t recno; + recno = atoi(p + 1); + key.data = &recno; + key.size = sizeof(recno); + } else { + key.data = xmalloc(p + 1, len - 1); + key.size = len - 1; + } +lkey: switch (command) { + case COMPARE: + getdata(dbp, &key, &keydata); + state = DATA; + break; + case GET: + get(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + case PUT: + state = DATA; + break; + case REMOVE: + rem(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + case SEQ: + seq(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + default: + err("line %lu: command doesn't take a key", + lineno); + } + break; + case 'o': + dump(dbp, p[1] == 'r'); + break; + default: + err("line %lu: %s: unknown command character", + lineno, p); + } + } +#ifdef STATISTICS + /* + * -l must be used (DB_LOCK must be set) for this to be + * used, otherwise a page will be locked and it will fail. + */ + if (type == DB_BTREE && oflags & DB_LOCK) + __bt_stat(dbp); +#endif + if (dbp->close(dbp)) + err("db->close: %s", strerror(errno)); + (void)close(ofd); + exit(0); +} + +#define NOOVERWRITE "put failed, would overwrite key\n" + +void +compare(db1, db2) + DBT *db1, *db2; +{ + register size_t len; + register u_char *p1, *p2; + + if (db1->size != db2->size) + printf("compare failed: key->data len %lu != data len %lu\n", + db1->size, db2->size); + + len = MIN(db1->size, db2->size); + for (p1 = db1->data, p2 = db2->data; len--;) + if (*p1++ != *p2++) { + printf("compare failed at offset %d\n", + p1 - (u_char *)db1->data); + break; + } +} + +void +get(dbp, kp) + DB *dbp; + DBT *kp; +{ + DBT data; + + switch (dbp->get(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err("line %lu: get: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "get failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else + (void)fprintf(stderr, "%d: %.*s: %s", + lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); +#undef NOSUCHKEY + break; + } +} + +void +getdata(dbp, kp, dp) + DB *dbp; + DBT *kp, *dp; +{ + switch (dbp->get(dbp, kp, dp, flags)) { + case 0: + return; + case -1: + err("line %lu: getdata: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + err("line %lu: getdata failed, no such key", lineno); + /* NOTREACHED */ + } +} + +void +put(dbp, kp, dp) + DB *dbp; + DBT *kp, *dp; +{ + switch (dbp->put(dbp, kp, dp, flags)) { + case 0: + break; + case -1: + err("line %lu: put: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); + break; + } +} + +void +rem(dbp, kp) + DB *dbp; + DBT *kp; +{ + switch (dbp->del(dbp, kp, flags)) { + case 0: + break; + case -1: + err("line %lu: rem: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "rem failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags != R_CURSOR) + (void)fprintf(stderr, "%d: %.*s: %s", + lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%d: rem of cursor failed\n", lineno); +#undef NOSUCHKEY + break; + } +} + +void +synk(dbp) + DB *dbp; +{ + switch (dbp->sync(dbp, flags)) { + case 0: + break; + case -1: + err("line %lu: synk: %s", lineno, strerror(errno)); + /* NOTREACHED */ + } +} + +void +seq(dbp, kp) + DB *dbp; + DBT *kp; +{ + DBT data; + + switch (dbp->seq(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err("line %lu: seq: %s", lineno, strerror(errno)); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "seq failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags == R_CURSOR) + (void)fprintf(stderr, "%d: %.*s: %s", + lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%d: seq (%s) failed\n", lineno, sflags(flags)); +#undef NOSUCHKEY + break; + } +} + +void +dump(dbp, rev) + DB *dbp; + int rev; +{ + DBT key, data; + int flags, nflags; + + if (rev) { + flags = R_LAST; + nflags = R_PREV; + } else { + flags = R_FIRST; + nflags = R_NEXT; + } + for (;; flags = nflags) + switch (dbp->seq(dbp, &key, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case 1: + goto done; + case -1: + err("line %lu: (dump) seq: %s", + lineno, strerror(errno)); + /* NOTREACHED */ + } +done: return; +} + +u_int +setflags(s) + char *s; +{ + char *p; + + for (; isspace(*s); ++s); + if (*s == '\n' || *s == '\0') + return (0); + if ((p = strchr(s, '\n')) != NULL) + *p = '\0'; + if (!strcmp(s, "R_CURSOR")) return (R_CURSOR); + if (!strcmp(s, "R_FIRST")) return (R_FIRST); + if (!strcmp(s, "R_IAFTER")) return (R_IAFTER); + if (!strcmp(s, "R_IBEFORE")) return (R_IBEFORE); + if (!strcmp(s, "R_LAST")) return (R_LAST); + if (!strcmp(s, "R_NEXT")) return (R_NEXT); + if (!strcmp(s, "R_NOOVERWRITE")) return (R_NOOVERWRITE); + if (!strcmp(s, "R_PREV")) return (R_PREV); + if (!strcmp(s, "R_SETCURSOR")) return (R_SETCURSOR); + + err("line %lu: %s: unknown flag", lineno, s); + /* NOTREACHED */ +} + +char * +sflags(flags) + int flags; +{ + switch (flags) { + case R_CURSOR: return ("R_CURSOR"); + case R_FIRST: return ("R_FIRST"); + case R_IAFTER: return ("R_IAFTER"); + case R_IBEFORE: return ("R_IBEFORE"); + case R_LAST: return ("R_LAST"); + case R_NEXT: return ("R_NEXT"); + case R_NOOVERWRITE: return ("R_NOOVERWRITE"); + case R_PREV: return ("R_PREV"); + case R_SETCURSOR: return ("R_SETCURSOR"); + } + + return ("UNKNOWN!"); +} + +DBTYPE +dbtype(s) + char *s; +{ + if (!strcmp(s, "btree")) + return (DB_BTREE); + if (!strcmp(s, "hash")) + return (DB_HASH); + if (!strcmp(s, "recno")) + return (DB_RECNO); + err("%s: unknown type (use btree, hash or recno)", s); + /* NOTREACHED */ +} + +void * +setinfo(type, s) + DBTYPE type; + char *s; +{ + static BTREEINFO ib; + static HASHINFO ih; + static RECNOINFO rh; + char *eq; + + if ((eq = strchr(s, '=')) == NULL) + err("%s: illegal structure set statement", s); + *eq++ = '\0'; + if (!isdigit(*eq)) + err("%s: structure set statement must be a number", s); + + switch (type) { + case DB_BTREE: + if (!strcmp("flags", s)) { + ib.flags = atoi(eq); + return (&ib); + } + if (!strcmp("cachesize", s)) { + ib.cachesize = atoi(eq); + return (&ib); + } + if (!strcmp("maxkeypage", s)) { + ib.maxkeypage = atoi(eq); + return (&ib); + } + if (!strcmp("minkeypage", s)) { + ib.minkeypage = atoi(eq); + return (&ib); + } + if (!strcmp("lorder", s)) { + ib.lorder = atoi(eq); + return (&ib); + } + if (!strcmp("psize", s)) { + ib.psize = atoi(eq); + return (&ib); + } + break; + case DB_HASH: + if (!strcmp("bsize", s)) { + ih.bsize = atoi(eq); + return (&ih); + } + if (!strcmp("ffactor", s)) { + ih.ffactor = atoi(eq); + return (&ih); + } + if (!strcmp("nelem", s)) { + ih.nelem = atoi(eq); + return (&ih); + } + if (!strcmp("cachesize", s)) { + ih.cachesize = atoi(eq); + return (&ih); + } + if (!strcmp("lorder", s)) { + ih.lorder = atoi(eq); + return (&ih); + } + break; + case DB_RECNO: + if (!strcmp("flags", s)) { + rh.flags = atoi(eq); + return (&rh); + } + if (!strcmp("cachesize", s)) { + rh.cachesize = atoi(eq); + return (&rh); + } + if (!strcmp("lorder", s)) { + rh.lorder = atoi(eq); + return (&rh); + } + if (!strcmp("reclen", s)) { + rh.reclen = atoi(eq); + return (&rh); + } + if (!strcmp("bval", s)) { + rh.bval = atoi(eq); + return (&rh); + } + if (!strcmp("psize", s)) { + rh.psize = atoi(eq); + return (&rh); + } + break; + } + err("%s: unknown structure value", s); + /* NOTREACHED */ +} + +void * +rfile(name, lenp) + char *name; + size_t *lenp; +{ + struct stat sb; + void *p; + int fd; + char *np; + + for (; isspace(*name); ++name); + if ((np = strchr(name, '\n')) != NULL) + *np = '\0'; + if ((fd = open(name, O_RDONLY, 0)) < 0 || + fstat(fd, &sb)) + err("%s: %s\n", name, strerror(errno)); +#ifdef NOT_PORTABLE + if (sb.st_size > (off_t)SIZE_T_MAX) + err("%s: %s\n", name, strerror(E2BIG)); +#endif + if ((p = (void *)malloc((u_int)sb.st_size)) == NULL) + err("%s", strerror(errno)); + (void)read(fd, p, (int)sb.st_size); + *lenp = sb.st_size; + (void)close(fd); + return (p); +} + +void * +xmalloc(text, len) + char *text; + size_t len; +{ + void *p; + + if ((p = (void *)malloc(len)) == NULL) + err("%s", strerror(errno)); + memmove(p, text, len); + return (p); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n"); + exit(1); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "dbtest: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/src/util/db2/test/hash1.tests/Makefile b/src/util/db2/test/hash1.tests/Makefile new file mode 100644 index 000000000..348a8f818 --- /dev/null +++ b/src/util/db2/test/hash1.tests/Makefile @@ -0,0 +1,43 @@ +# @(#)Makefile 8.16 (Berkeley) 11/20/95 + +ALL = driver2 tcreat3 tdel thash4 tread2 tseq tverify +OBJ = driver2.o tcreat3.o tdel.o thash4.o tread2.o tseq.o tverify.o +CC = gcc + +all: ${ALL} + +# Uncomment the STAT line get hash and btree statistical use info. This +# also forces ld to load the btree debug functions for use by gdb, which +# is useful. The db library has to be compiled with -DSTATISTICS as well. +INC= -I${PORTDIR}/include -I${PORTDIR} +OORG= -g +#STAT= -DSTATISTICS +CFLAGS= -D__DBINTERFACE_PRIVATE -DDEBUG ${STAT} ${OORG} ${INC} + +tcreat3: tcreat3.o ${PORTDIR}/libdb.a + ${CC} -o $@ tcreat3.o ${PORTDIR}/libdb.a + +tdel: tdel.o ${PORTDIR}/libdb.a + ${CC} -o $@ tdel.o ${PORTDIR}/libdb.a + +thash4: thash4.o ${PORTDIR}/libdb.a + ${CC} -o $@ thash4.o ${PORTDIR}/libdb.a + +tread2: tread2.o ${PORTDIR}/libdb.a + ${CC} -o $@ tread2.o ${PORTDIR}/libdb.a + +tseq: tseq.o ${PORTDIR}/libdb.a + ${CC} -o $@ tseq.o ${PORTDIR}/libdb.a + +tverify: tverify.o ${PORTDIR}/libdb.a + ${CC} -o $@ tverify.o ${PORTDIR}/libdb.a + +driver2: driver2.o ${PORTDIR}/libdb.a + ${CC} -o $@ driver2.o ${PORTDIR}/libdb.a + +strerror.o: ${PORTDIR}/clib/strerror.c + ${CC} -c ${PORTDIR}/clib/strerror.c + +clean: + rm -f *.core gmon.out ${ALL} ${OBJ} t1 t2 t3 + diff --git a/src/util/db2/test/hash1.tests/driver2.c b/src/util/db2/test/hash1.tests/driver2.c new file mode 100644 index 000000000..074f9eae0 --- /dev/null +++ b/src/util/db2/test/hash1.tests/driver2.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)driver2.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Test driver, to try to tackle the large ugly-split problem. + */ + +#include +#include +#include "ndbm.h" + +int my_hash(key, len) + char *key; + int len; +{ + return(17); /* So I'm cruel... */ +} + +main(argc, argv) + int argc; +{ + DB *db; + DBT key, content; + char keybuf[2049]; + char contentbuf[2049]; + char buf[256]; + int i; + HASHINFO info; + + info.bsize = 1024; + info.ffactor = 5; + info.nelem = 1; + info.cachesize = NULL; +#ifdef HASH_ID_PROGRAM_SPECIFIED + info.hash_id = HASH_ID_PROGRAM_SPECIFIED; + info.hash_func = my_hash; +#else + info.hash = my_hash; +#endif + info.lorder = 0; + if (!(db = dbopen("bigtest", O_RDWR | O_CREAT, 0644, DB_HASH, &info))) { + sprintf(buf, "dbopen: failed on file bigtest"); + perror(buf); + exit(1); + } + srand(17); + key.data = keybuf; + content.data = contentbuf; + memset(keybuf, '\0', sizeof(keybuf)); + memset(contentbuf, '\0', sizeof(contentbuf)); + for (i=1; i <= 500; i++) { + key.size = 128 + (rand()&1023); + content.size = 128 + (rand()&1023); +/* printf("%d: Key size %d, data size %d\n", i, key.size, + content.size); */ + sprintf(keybuf, "Key #%d", i); + sprintf(contentbuf, "Contents #%d", i); + if ((db->put)(db, &key, &content, R_NOOVERWRITE)) { + sprintf(buf, "dbm_store #%d", i); + perror(buf); + } + } + if ((db->close)(db)) { + perror("closing hash file"); + exit(1); + } + exit(0); +} + + + diff --git a/src/util/db2/test/hash1.tests/makedb.sh b/src/util/db2/test/hash1.tests/makedb.sh new file mode 100644 index 000000000..15901de19 --- /dev/null +++ b/src/util/db2/test/hash1.tests/makedb.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# @(#)makedb.sh 8.1 (Berkeley) 6/4/93 + +awk '{i++; print $0; print i;}' /usr/local/lib/dict/words > WORDS +ls /bin /usr/bin /usr/ucb /etc | egrep '^(...|....|.....|......)$' | \ +sort | uniq | \ +awk '{ + printf "%s\n", $0 + for (i = 0; i < 1000; i++) + printf "%s+", $0 + printf "\n" +}' > LONG.DATA diff --git a/src/util/db2/test/hash1.tests/tcreat3.c b/src/util/db2/test/hash1.tests/tcreat3.c new file mode 100644 index 000000000..3aaab99cb --- /dev/null +++ b/src/util/db2/test/hash1.tests/tcreat3.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tcreat3.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include "db-int.h" + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int trash; + + int i = 0; + + argv++; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.nelem = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", + O_CREAT|O_TRUNC|O_RDWR, 0600, DB_HASH, &ctl))){ + /* create table */ + fprintf(stderr, "cannot create: hash table (size %d)\n", + INITIAL); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + exit(1); + } + } + + (dbp->close)(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash1.tests/tdel.c b/src/util/db2/test/hash1.tests/tdel.c new file mode 100644 index 000000000..b542d8ef3 --- /dev/null +++ b/src/util/db2/test/hash1.tests/tdel.c @@ -0,0 +1,122 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tdel.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include "db-int.h" +#include + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +/* Usage: thash pagesize fillfactor file */ +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int stat; + + int i = 0; + + argv++; + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.cachesize = 1024 * 1024; /* 1 MEG */ + ctl.lorder = 0; + argc -= 2; + if (!(dbp = dbopen( NULL, O_CREAT|O_RDWR, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot create: hash table size %d)\n", + INITIAL); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + exit(1); + } + } + + if ( --argc ) { + fp = fopen ( argv[0], "r"); + i = 0; + while ( fgets(wp1, 8192, fp) && + fgets(wp2, 8192, fp) && + i++ < MAXWORDS) { + key.size = strlen(wp1); + stat = (dbp->del)(dbp, &key, 0); + if (stat) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } + } + fclose(fp); + } + (dbp->close)(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash1.tests/testit b/src/util/db2/test/hash1.tests/testit new file mode 100644 index 000000000..c80dc4e69 --- /dev/null +++ b/src/util/db2/test/hash1.tests/testit @@ -0,0 +1,154 @@ +#!/bin/csh -f +# +# @(#)testit 8.1 (Berkeley) 6/4/93 +# + +echo "" +echo "PAGE FILL " +set name=WORDS + set i = 256 + foreach j ( 11 14 21 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + set i = 512 + foreach j ( 21 28 43 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + set i = 1024 + foreach j ( 43 57 85 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + set i = 2048 + foreach j ( 85 114 171 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + set i = 4096 + foreach j ( 171 228 341 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + set i = 8192 + foreach j ( 341 455 683 ) + echo "thash4 $i $j" + ./thash4 $i $j 25000 65536 $name < $name + end + echo "PAGE FILL " + set i = 256 + foreach j ( 11 14 21 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 512 + foreach j ( 21 28 43 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 1024 + foreach j ( 43 57 85 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 2048 + foreach j ( 85 114 171 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 4096 + foreach j ( 171 228 341 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 8192 + foreach j ( 341 455 683 ) + echo "$i"_"$j" + ./tcreat3 $i $j 25000 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end +set name=LONG.DATA + set i = 1024 + foreach j ( 1 2 4 ) + echo ./thash4 $i $j 600 65536 $name + ./thash4 $i $j 600 65536 $name < $name + end + + set i = 2048 + foreach j ( 1 2 4 ) + echo ./thash4 $i $j 600 65536 $name + ./thash4 $i $j 600 65536 $name < $name + end + set i = 4096 + foreach j ( 1 2 4 ) + echo ./thash4 $i $j 600 65536 $name + ./thash4 $i $j 600 65536 $name < $name + end + set i = 8192 + foreach j ( 2 4 8 ) + echo ./thash4 $i $j 600 65536 $name + ./thash4 $i $j 600 65536 $name < $name + end + echo "PAGE FILL " + set i = 1024 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + ./tcreat3 $i $j 600 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 2048 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + ./tcreat3 $i $j 600 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 4096 + foreach j ( 1 2 4 ) + echo "$i"_"$j" + ./tcreat3 $i $j 600 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + set i = 8192 + foreach j ( 2 4 8 ) + echo "$i"_"$j" + ./tcreat3 $i $j 600 $name < $name + ./tread2 65536 < $name + ./tverify $name < $name + ./tseq > /dev/null + ./tdel $i $j $name < $name + end + +./driver2 diff --git a/src/util/db2/test/hash1.tests/thash4.c b/src/util/db2/test/hash1.tests/thash4.c new file mode 100644 index 000000000..b0d4b42e3 --- /dev/null +++ b/src/util/db2/test/hash1.tests/thash4.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)thash4.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include "db-int.h" + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +/* Usage: thash pagesize fillfactor file */ +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + HASHINFO ctl; + FILE *fp; + int stat; + time_t t; + + int i = 0; + + argv++; + ctl.hash = NULL; + ctl.bsize = atoi(*argv++); + ctl.ffactor = atoi(*argv++); + ctl.nelem = atoi(*argv++); + ctl.cachesize = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( NULL, O_CREAT|O_RDWR, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot create: hash table size %d)\n", + INITIAL); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + +/* + * enter key/data pair into the table + */ + if ((dbp->put)(dbp, &key, &item, R_NOOVERWRITE) != NULL) { + fprintf(stderr, "cannot enter: key %s\n", + item.data); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + } + + if ( --argc ) { + fp = fopen ( argv[0], "r"); + i = 0; + while ( fgets(wp1, 256, fp) && + fgets(wp2, 8192, fp) && + i++ < MAXWORDS) { + + key.size = strlen(wp1); + stat = (dbp->get)(dbp, &key, &res, 0); + if (stat < 0 ) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + fprintf(stderr, "\terrno: %d\n", errno); + exit(1); + } + } + fclose(fp); + } + dbp->close(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash1.tests/tread2.c b/src/util/db2/test/hash1.tests/tread2.c new file mode 100644 index 000000000..44acaa93d --- /dev/null +++ b/src/util/db2/test/hash1.tests/tread2.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tread2.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include "db-int.h" + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +typedef struct { /* info to be stored */ + int num, siz; +} info; + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + HASHINFO ctl; + int stat; + + int i = 0; + + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = 64; + ctl.ffactor = 1; + ctl.cachesize = atoi(*argv++); + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + + key.data = wp1; + item.data = wp2; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + item.size = strlen(wp2); + + stat = (dbp->get)(dbp, &key, &res,0); + if (stat < 0) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + exit(1); + } + } + (dbp->close)(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash1.tests/tseq.c b/src/util/db2/test/hash1.tests/tseq.c new file mode 100644 index 000000000..5eee5de61 --- /dev/null +++ b/src/util/db2/test/hash1.tests/tseq.c @@ -0,0 +1,88 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tseq.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include "db-int.h" + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + + +char wp[8192]; +char cp[8192]; +main(argc, argv) +char **argv; +{ + DBT item, key, res; + DB *dbp; + FILE *fp; + int stat; + + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, NULL))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + +/* +* put info in structure, and structure in the item +*/ + for ( stat = (dbp->seq) (dbp, &res, &item, 1 ); + stat == 0; + stat = (dbp->seq) (dbp, &res, &item, 0 ) ) { + + memcpy ( wp, res.data, res.size ); + wp[res.size] = 0; + memcpy ( cp, item.data, item.size ); + cp[item.size] = 0; + + printf ( "%s %s\n", wp, cp ); + } + (dbp->close)(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash1.tests/tverify.c b/src/util/db2/test/hash1.tests/tverify.c new file mode 100644 index 000000000..a5a5dfd18 --- /dev/null +++ b/src/util/db2/test/hash1.tests/tverify.c @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Margo Seltzer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tverify.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include +#include +#include +#include "db-int.h" + +#define INITIAL 25000 +#define MAXWORDS 25000 /* # of elements in search table */ + +typedef struct { /* info to be stored */ + int num, siz; +} info; + +char wp1[8192]; +char wp2[8192]; +main(argc, argv) +char **argv; +{ + DBT key, res; + DB *dbp; + HASHINFO ctl; + int trash; + int stat; + + int i = 0; + + ctl.nelem = INITIAL; + ctl.hash = NULL; + ctl.bsize = 64; + ctl.ffactor = 1; + ctl.cachesize = 1024 * 1024; /* 1 MEG */ + ctl.lorder = 0; + if (!(dbp = dbopen( "hashtest", O_RDONLY, 0400, DB_HASH, &ctl))) { + /* create table */ + fprintf(stderr, "cannot open: hash table\n" ); + exit(1); + } + + key.data = wp1; + while ( fgets(wp1, 8192, stdin) && + fgets(wp2, 8192, stdin) && + i++ < MAXWORDS) { +/* +* put info in structure, and structure in the item +*/ + key.size = strlen(wp1); + + stat = (dbp->get)(dbp, &key, &res,0); + if (stat < 0) { + fprintf ( stderr, "Error retrieving %s\n", key.data ); + exit(1); + } else if ( stat > 0 ) { + fprintf ( stderr, "%s not found\n", key.data ); + exit(1); + } + if ( memcmp ( res.data, wp2, res.size ) ) { + fprintf ( stderr, "data for %s is incorrect. Data was %s. Should have been %s\n", key.data, res.data, wp2 ); + } + } + (dbp->close)(dbp); + exit(0); +} diff --git a/src/util/db2/test/hash2.tests/README b/src/util/db2/test/hash2.tests/README new file mode 100644 index 000000000..f29ccf7e1 --- /dev/null +++ b/src/util/db2/test/hash2.tests/README @@ -0,0 +1,72 @@ +# @(#)README 8.1 (Berkeley) 6/4/93 + +This package implements a superset of the hsearch and dbm/ndbm libraries. + +Test Programs: + All test programs which need key/data pairs expect them entered + with key and data on separate lines + + tcreat3.c + Takes + bucketsize (bsize), + fill factor (ffactor), and + initial number of elements (nelem). + Creates a hash table named hashtest containing the + keys/data pairs entered from standard in. + thash4.c + Takes + bucketsize (bsize), + fill factor (ffactor), + initial number of elements (nelem) + bytes of cache (ncached), and + file from which to read data (fname) + Creates a table from the key/data pairs on standard in and + then does a read of each key/data in fname + tdel.c + Takes + bucketsize (bsize), and + fill factor (ffactor). + file from which to read data (fname) + Reads each key/data pair from fname and deletes the + key from the hash table hashtest + tseq.c + Reads the key/data pairs in the file hashtest and writes them + to standard out. + tread2.c + Takes + butes of cache (ncached). + Reads key/data pairs from standard in and looks them up + in the file hashtest. + tverify.c + Reads key/data pairs from standard in, looks them up + in the file hashtest, and verifies that the data is + correct. + +NOTES: + +The file search.h is provided for using the hsearch compatible interface +on BSD systems. On System V derived systems, search.h should appear in +/usr/include. + +The man page ../man/db.3 explains the interface to the hashing system. +The file hash.ps is a postscript copy of a paper explaining +the history, implementation, and performance of the hash package. + +"bugs" or idiosyncracies + +If you have a lot of overflows, it is possible to run out of overflow +pages. Currently, this will cause a message to be printed on stderr. +Eventually, this will be indicated by a return error code. + +If you are using the ndbm interface and exit without flushing or closing the +file, you may lose updates since the package buffers all writes. Also, +the db interface only creates a single database file. To avoid overwriting +the user's original file, the suffix ".db" is appended to the file name +passed to dbm_open. Additionally, if your code "knows" about the historic +.dir and .pag files, it will break. + +There is a fundamental difference between this package and the old hsearch. +Hsearch requires the user to maintain the keys and data in the application's +allocated memory while hash takes care of all storage management. The down +side is that the byte strings passed in the ENTRY structure must be null +terminated (both the keys and the data). diff --git a/src/util/db2/test/hash2.tests/bigtest.c b/src/util/db2/test/hash2.tests/bigtest.c new file mode 100644 index 000000000..e6d4108b7 --- /dev/null +++ b/src/util/db2/test/hash2.tests/bigtest.c @@ -0,0 +1,76 @@ +#include "db-int.h" +#include +#include +#include +#include + +int +main(void) +{ + HASHINFO info; + DB *db; + DBT key, value, returned; + int *data; + int n, i; + + info.bsize = 512; + info.cachesize = 500; + info.lorder = 0; + info.ffactor = 4; + info.nelem = 0; + info.hash = NULL; + + db = dbopen("big2.db", O_RDWR|O_CREAT|O_TRUNC, 0664, DB_HASH, &info); + data = malloc(800 * sizeof(int)); + for (n = 0; n < 800; n++) + data[n] = 0xDEADBEEF; + key.size = sizeof(int); + key.data = &n; + value.size = 800 * sizeof(int); + value.data = (void *)data; + + for (n = 0; n < 200000; n++) { + returned.data = NULL; + if (n == 4627) + printf(""); + if (n % 50 == 0) + printf("put n = %d\n", n); + if (db->put(db, &key, &value, 0) != 0) + printf("put error, n = %d\n", n); + if (db->get(db, &key, &returned, 0) != 0) + printf("Immediate get error, n = %d\n", n); + assert (returned.size == 3200); + for (i = 0; i < 800; i++) + if (((int *)returned.data)[i] != 0xDEADBEEF) + printf("ERRORRRRRR!!!\n"); + + } + + for (n = 0; n < 200000; n++) { + if (n % 50 == 0) + printf("seq n = %d\n", n); + if ((db->seq(db, &key, &returned, 0)) != 0) + printf("Seq error, n = %d\n", n); + + assert(returned.size == 3200); + + for (i = 0; i < 800; i++) + if (((int *)returned.data)[i] != 0xDEADBEEF) + printf("ERRORRRRRR!!! seq %d\n", n); + } + + for (n = 0; n < 2000; n++) { + if (n % 50 == 0) + printf("get n = %d\n", n); + if (db->get(db, &key, &returned, 0) != 0) + printf("Late get error, n = %d\n", n); + assert(returned.size == 1200); + for (i = 0; i < 300; i++) + if (((int *)returned.data)[i] != 0xDEADBEEF) + printf("ERRORRRRRR!!!, get %d\n", n); + } + db->close(db); + free(value.data); + return(0); +} + diff --git a/src/util/db2/test/hash2.tests/passtest.c b/src/util/db2/test/hash2.tests/passtest.c new file mode 100644 index 000000000..d6277ca52 --- /dev/null +++ b/src/util/db2/test/hash2.tests/passtest.c @@ -0,0 +1,184 @@ +#include "db-int.h" +#include +#include +#include + +extern int hash_expansions; + +int +main(void) +{ + FILE *keys, *vals; + DB *db; + DBT key, val; + char *key_line, *val_line, *get_key, *get_val, *old, *key2; + HASHINFO passwd; + int n = 0, i = 0, expected; + + key_line = (char *)malloc(100); + val_line = (char *)malloc(300); + old = (char *)malloc(300); + + keys = fopen("yp.keys", "rt"); + vals = fopen("yp.total", "rt"); + + passwd.bsize = 1024; + passwd.cachesize = 1024 * 1024; + passwd.ffactor = 10; + passwd.hash = NULL; + passwd.nelem = 0; + passwd.lorder = 4321; + + + db = dbopen("/usr/tmp/passwd.db", O_RDWR|O_CREAT|O_TRUNC, 0664, DB_HASH, + &passwd); + if (!db) { + fprintf(stderr, "create_db: couldn't create database file\n"); + exit(1); + } + + while ((key_line = fgets(key_line, 100, keys)) != NULL) { + if (n % 1000 == 0) + fprintf(stderr, "Putting #%d.\n", n); + n++; + fgets(val_line, 300, vals); + key.size = strlen(key_line); + key.data = (void *)key_line; + val.size = strlen(val_line); + val.data = (void *)val_line; + if (db->put(db, &key, &val, 0) != 0) + fprintf(stderr, "Put error, n = %d\n", n); + if (db->get(db, &key, &val, 0) != 0) + fprintf(stderr, "Immediate get error, n = %d\n", n); + } + fprintf(stderr, "Done with put!\n"); + free(key_line); + free(val_line); + fclose(keys); + fclose(vals); + db->close(db); + + + + + keys = fopen("yp.keys", "rt"); + vals = fopen("yp.total", "rt"); + get_key = (char *)malloc(100); + get_val = (char *)malloc(300); + + db = dbopen("/usr/tmp/passwd.db", O_RDWR, 0664, DB_HASH, &passwd); + if (!db) + fprintf(stderr, "Could not open db!\n"); + n = 0; + while ((get_key = fgets(get_key, 100, keys)) != NULL) { + n++; + if (n % 1000 == 0) + fprintf(stderr, "Getting #%d.\n", n); + key.size = strlen(get_key); + key.data = (void *)get_key; + if (db->get(db, &key, &val, 0) != 0) + fprintf(stderr, "Retrieval error on %s\n", get_key); + fgets(get_val, 300, vals); + if (memcmp(val.data, (void *)get_val, val.size)) { + fprintf(stderr, "Unmatched get on %s.\n", get_key); + fprintf(stderr, "Input = %s\nOutput = %s\n", get_val, + (char *)val.data); + } + } + expected = n; + fclose(vals); + fclose(keys); + free(get_key); + free(get_val); + db->close(db); + + + + + get_key = (char *)malloc(100); + get_val = (char *)malloc(300); + + db = dbopen("/usr/tmp/passwd.db", O_RDWR, 0664, DB_HASH, &passwd); + if (!db) + fprintf(stderr, "Could not open db!\n"); + n = 0; + for (;;) { + n++; + if (n % 1000 == 0) + fprintf(stderr, "Sequence getting #%d.\n", n); + if (db->seq(db, &key, &val, 0) != 0) { + fprintf(stderr, + "Exiting sequence retrieve; n = %d, expected = %d\n", + n - 1 , expected); + break; + } + } + free(get_key); + free(get_val); + db->close(db); + + get_key = (char *)malloc(100); + key2 = (char *)malloc(100); + + keys = fopen("yp.keys", "rt"); + vals = fopen("yp.total", "rt"); + + db = dbopen("/usr/tmp/passwd.db", O_RDWR, 0664, DB_HASH, &passwd); + n = 0; + while ((get_key = fgets(get_key, 100, keys)) != NULL) { + if (n % 1000 == 0) + fprintf(stderr, "Deleting #%d.\n", n); + n+=2; + key2 = fgets(get_key, 100, keys); + if (!key2) + break; + key.data = (void *)key2; + key.size = strlen(key2); + if (db->del(db, &key, 0) != 0) + fprintf(stderr, "Delete error on %d", n); + } + + db->close(db); + free(get_key); + free(key2); + fclose(keys); + fclose(vals); + + get_key = (char *)malloc(100); + key2 = (char *)malloc(100); + get_val = (char *)malloc(300); + + keys = fopen("yp.keys", "rt"); + vals = fopen("yp.total", "rt"); + + db = dbopen("/usr/tmp/passwd.db", O_RDWR, 0664, DB_HASH, &passwd); + n = 0; + while ((get_key = fgets(get_key, 100, keys)) != NULL) { + n += 2; + if (n % 1000 == 0) + fprintf(stderr, "Re-retrieving #%d.\n", n); + key2 = fgets(key2, 100, keys); + if (!key2) + break; + key.data = (void *)get_key; + key.size = strlen(get_key); + if (db->get(db, &key, &val, 0) != 0) + fprintf(stderr, "Retrieval after delete error on %d\n", n); + fgets(get_val, 300, vals); + if (memcmp(val.data, (void *)get_val, val.size)) { + fprintf(stderr, "Unmatched get after delete on %s.\n", get_key); + fprintf(stderr, "Input = %s\nOutput = %s\n", get_val, + (char *)val.data); + } + fgets(get_val, 300, vals); + } + + db->close(db); + free(get_key); + free(key2); + free(get_val); + fclose(keys); + fclose(vals); + + exit(0); +} diff --git a/src/util/db2/test/hash2.tests/passwd/genpass.c b/src/util/db2/test/hash2.tests/passwd/genpass.c new file mode 100644 index 000000000..da3767687 --- /dev/null +++ b/src/util/db2/test/hash2.tests/passwd/genpass.c @@ -0,0 +1,23 @@ +#include +#include + +void +main(int argc, char **argv) +{ + int i,j,n; + char *pass[8], r; + + n = atoi(argv[1]); + + srandom(101173); + for (i = 0; i < n; i++) { + for (j = 0; j < 8; j++) { + r = random() % 122; + while (r < 48) + r += random() % (122 - r); + printf("%c", r); + } + printf("\n"); + } +} + diff --git a/src/util/db2/test/run.test b/src/util/db2/test/run.test new file mode 100644 index 000000000..393aedcad --- /dev/null +++ b/src/util/db2/test/run.test @@ -0,0 +1,717 @@ +#!/bin/sh - +# +# @(#)run.test 8.13 (Berkeley) 11/2/95 +# + +# db regression tests +main() +{ + + PROG=./dbtest + TMP1=${TMPDIR-.}/t1 + TMP2=${TMPDIR-.}/t2 + TMP3=${TMPDIR-.}/t3 + + if [ \! -z "$WORDLIST" -a -f "$WORDLIST" ]; then + DICT=$WORDLIST + elif [ -f /usr/local/lib/dict/words ]; then + DICT=/usr/local/lib/dict/words + elif [ -f /usr/share/dict/words ]; then + DICT=/usr/share/dict/words + elif [ -f /usr/dict/words ]; then + DICT=/usr/dict/words + elif [ -f /usr/share/lib/dict/words ]; then + DICT=/usr/share/lib/dict/words + else + echo 'run.test: no dictionary' + exit 1 + fi + + if [ $# -eq 0 ]; then + for t in 1 2 3 4 5 6 7 8 9 10 11 12 13 20; do + test$t + done + else + while [ $# -gt 0 ] + do case "$1" in + test*) + $1;; + [0-9]*) + test$1;; + btree) + for t in 1 2 3 7 8 9 10 12 13; do + test$t + done;; + hash) + for t in 1 2 3 8 13 20; do + test$t + done;; + recno) + for t in 1 2 3 4 5 6 7 10 11; do + test$t + done;; + *) + echo "run.test: unknown test $1" + echo "usage: run.test test# | type" + exit 1 + esac + shift + done + fi + rm -f $TMP1 $TMP2 $TMP3 + exit 0 +} + +# Take the first hundred entries in the dictionary, and make them +# be key/data pairs. +test1() +{ + echo "Test 1: btree, hash: small key, small data pairs" + sed 200q $DICT > $TMP1 + for type in btree hash; do + rm -f $TMP2 $TMP3 + for i in `sed 200q $DICT`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test1: type $type: failed" + exit 1 + fi + done + echo "Test 1: recno: small key, small data pairs" + rm -f $TMP2 $TMP3 + sed 200q $DICT | + awk '{ + ++i; + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test1: type recno: failed" + exit 1 + fi +} + +# Take the first 200 entries in the dictionary, and give them +# each a medium size data entry. +test2() +{ + echo "Test 2: btree, hash: small key, medium data pairs" + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' > $TMP1 + for type in hash btree; do + rm -f $TMP2 $TMP3 + for i in `sed 200q $DICT`; do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test2: type $type: failed" + exit 1 + fi + done + echo "Test 2: recno: small key, medium data pairs" + rm -f $TMP2 $TMP3 + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test2: type recno: failed" + exit 1 + fi +} + +# Insert the programs in /bin with their paths as their keys. +test3() +{ + echo "Test 3: hash: small key, big data pairs" + rm -f $TMP1 + (find /bin -type f -exec test -r {} \; -print | xargs cat) > $TMP1 + for type in hash; do + rm -f $TMP2 $TMP3 + for i in `find /bin -type f -exec test -r {} \; -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test3: $type: failed" + exit 1 + fi + done + echo "Test 3: btree: small key, big data pairs" + for psize in 512 16384 65536; do + echo " page size $psize" + for type in btree; do + rm -f $TMP2 $TMP3 + for i in `find /bin -type f -exec test -r {} \; -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done > $TMP2 + $PROG -i psize=$psize -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test3: $type: page size $psize: failed" + exit 1 + fi + done + done + echo "Test 3: recno: big data pairs" + rm -f $TMP2 $TMP3 + find /bin -type f -exec test -r {} \; -print | + awk '{ + ++i; + printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i); + }' > $TMP2 + for psize in 512 16384 65536; do + echo " page size $psize" + $PROG -i psize=$psize -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test3: recno: page size $psize: failed" + exit 1 + fi + done +} + +# Do random recno entries. +test4() +{ + echo "Test 4: recno: random entries" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 37; i <= 37 + 88 * 17; i += 17) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 1; i <= 15; ++i) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 19234; i <= 19234 + 61 * 27; i += 27) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit + }' > $TMP1 + rm -f $TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 37; + incr = 17; + } + { + printf("p\nk%d\nd%s\n", i, $0); + if (i == 19234 + 61 * 27) + exit; + if (i == 37 + 88 * 17) { + i = 1; + incr = 1; + } else if (i == 15) { + i = 19234; + incr = 27; + } else + i += incr; + } + END { + for (i = 37; i <= 37 + 88 * 17; i += 17) + printf("g\nk%d\n", i); + for (i = 1; i <= 15; ++i) + printf("g\nk%d\n", i); + for (i = 19234; i <= 19234 + 61 * 27; i += 27) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test4: type recno: failed" + exit 1 + fi +} + +# Do reverse order recno entries. +test5() +{ + echo "Test 5: recno: reverse order entries" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk ' { + for (i = 1500; i; --i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 1500; + } + { + printf("p\nk%d\nd%s\n", i, $0); + --i; + } + END { + for (i = 1500; i; --i) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test5: type recno: failed" + exit 1 + fi +} + +# Do alternating order recno entries. +test6() +{ + echo "Test 6: recno: alternating order entries" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk ' { + for (i = 1; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 2; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + cat $TMP1 | + awk 'BEGIN { + i = 1; + even = 0; + } + { + printf("p\nk%d\nd%s\n", i, $0); + i += 2; + if (i >= 1200) { + if (even == 1) + exit; + even = 1; + i = 2; + } + } + END { + for (i = 1; i < 1200; ++i) + printf("g\nk%d\n", i); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + sort -o $TMP1 $TMP1 + sort -o $TMP3 $TMP3 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test6: type recno: failed" + exit 1 + fi +} + +# Delete cursor record +test7() +{ + echo "Test 7: btree, recno: delete cursor record" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 120; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + printf("%05d: input key %d: %s\n", 120, 120, $0); + printf("seq failed, no such key\n"); + printf("%05d: input key %d: %s\n", 1, 1, $0); + printf("%05d: input key %d: %s\n", 2, 2, $0); + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 120) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_NEXT\n"); + for (i = 1; i <= 120; ++i) + printf("s\n"); + printf("fR_CURSOR\ns\nk120\n"); + printf("r\n"); + printf("fR_NEXT\ns\n"); + printf("fR_CURSOR\ns\nk1\n"); + printf("r\n"); + printf("fR_FIRST\ns\n"); + }' > $TMP2 + $PROG -o $TMP3 recno $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test7: type $type: failed" + exit 1 + fi + done +} + +# Make sure that overflow pages are reused. +test8() +{ + echo "Test 8: btree, hash: repeated small key, big data pairs" + rm -f $TMP1 + echo "" | + awk 'BEGIN { + for (i = 1; i <= 10; ++i) { + printf("p\nkkey1\nD/bin/sh\n"); + printf("p\nkkey2\nD/bin/csh\n"); + if (i % 8 == 0) { + printf("c\nkkey2\nD/bin/csh\n"); + printf("c\nkkey1\nD/bin/sh\n"); + printf("e\t%d of 10 (comparison)\n", i); + } else + printf("e\t%d of 10 \n", i); + printf("r\nkkey1\nr\nkkey2\n"); + } + }' > $TMP1 + $PROG btree $TMP1 +# $PROG hash $TMP1 + # No explicit test for success. +} + +# Test btree duplicate keys +test9() +{ + echo "Test 9: btree: duplicate keys" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 543; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + + for type in btree; do + cat $TMP1 | + awk '{ + if (i++ % 2) + printf("p\nkduplicatekey\nd%s\n", $0); + else + printf("p\nkunique%dkey\nd%s\n", i, $0); + } + END { + printf("o\n"); + }' > $TMP2 + $PROG -iflags=1 -o $TMP3 $type $TMP2 + sort -o $TMP3 $TMP3 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test9: type $type: failed" + exit 1 + fi + done +} + +# Test use of cursor flags without initialization +test10() +{ + echo "Test 10: btree, recno: test cursor flag use" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 20; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + + # Test that R_CURSOR doesn't succeed before cursor initialized + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\nr\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 + if [ -s $TMP3 ] ; then + echo "Test 10: delete: R_CURSOR SHOULD HAVE FAILED" + exit 1 + fi + done + for type in btree recno; do + cat $TMP1 | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\np\nk1\ndsome data\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 + if [ -s $TMP3 ] ; then + echo "Test 10: put: R_CURSOR SHOULD HAVE FAILED" + exit 1 + fi + done +} + +# Test insert in reverse order. +test11() +{ + echo "Test 11: recno: reverse order insert" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 779; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' > $TMP1 + rm -f $TMP2 $TMP3 + + for type in recno; do + cat $TMP1 | + awk '{ + if (i == 0) { + i = 1; + printf("p\nk1\nd%s\n", $0); + printf("%s\n", "fR_IBEFORE"); + } else + printf("p\nk1\nd%s\n", $0); + } + END { + printf("or\n"); + }' > $TMP2 + $PROG -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test11: type $type: failed" + exit 1 + fi + done +} + +# Take the first 20000 entries in the dictionary, reverse them, and give +# them each a small size data entry. Use a small page size to make sure +# the btree split code gets hammered. +test12() +{ + if ( rev < /dev/null ) > /dev/null 2>&1 ; then + : + else + echo "Test 12: skipped, rev not found" + return + fi + echo "Test 12: btree: lots of keys, small page size" + mdata=abcdefghijklmnopqrstuvwxy + echo $mdata | + awk '{ for (i = 1; i < 20001; ++i) print $0 }' > $TMP1 + for type in btree; do + rm -f $TMP2 $TMP3 + for i in `sed 20000q $DICT | rev`; do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done > $TMP2 + $PROG -i psize=512 -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test12: type $type: failed" + exit 1 + fi + done +} + +# Test different byte orders. +test13() +{ + echo "Test 13: btree, hash: differing byte orders" + sed 50q $DICT > $TMP1 + for order in 1234 4321; do + for type in btree hash; do + rm -f byte.file $TMP2 $TMP3 + for i in `sed 50q $DICT`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done > $TMP2 + $PROG -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test13: $type/$order put failed" + exit 1 + fi + for i in `sed 50q $DICT`; do + echo g + echo k$i + done > $TMP2 + $PROG -s \ + -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test13: $type/$order get failed" + exit 1 + fi + done + done + rm -f byte.file +} + +# Try a variety of bucketsizes and fill factors for hashing +test20() +{ + echo\ + "Test 20: hash: bucketsize, fill factor; nelem 25000 cachesize 65536" + echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | + awk '{ + for (i = 1; i <= 10000; ++i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("%s\n", s); + } + exit; + }' > $TMP1 + sed 10000q $DICT | + awk 'BEGIN { + ds="abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" + } + { + if (++i % 34) + s = substr(ds, 1, i % 34); + else + s = substr(ds, 1); + printf("p\nk%s\nd%s\n", $0, s); + }' > $TMP2 + sed 10000q $DICT | + awk '{ + ++i; + printf("g\nk%s\n", $0); + }' >> $TMP2 + bsize=256 + for ffactor in 11 14 21; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done + bsize=512 + for ffactor in 21 28 43; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done + bsize=1024 + for ffactor in 43 57 85; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done + bsize=2048 + for ffactor in 85 114 171; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done + bsize=4096 + for ffactor in 171 228 341; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done + bsize=8192 + for ffactor in 341 455 683; do + echo " bucketsize $bsize, fill factor $ffactor" + $PROG -o$TMP3 \ + -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ + hash $TMP2 + if (cmp -s $TMP1 $TMP3) ; then : + else + echo "test20: type hash:\ +bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" + exit 1 + fi + done +} + +main $* diff --git a/src/util/dyn/ChangeLog b/src/util/dyn/ChangeLog new file mode 100644 index 000000000..9fac92665 --- /dev/null +++ b/src/util/dyn/ChangeLog @@ -0,0 +1,13 @@ +Mon Jul 22 04:20:48 1996 Marc Horowitz + + * dyn_insert.c (DynInsert): what used to be #ifdef POSIX, should + be #ifdef HAVE_MEMMOVE + +Tue Jul 9 19:30:40 1996 Marc Horowitz + + * configure.in (DEPLIBS): AC_SUBST() it, to hack around an + incorrect assumption in aclocal.m4 + * Makefile.in (DONE): add rules and macros to support shared + libraries + + diff --git a/src/util/dyn/Imakefile b/src/util/dyn/Imakefile new file mode 100644 index 000000000..471cf5bc8 --- /dev/null +++ b/src/util/dyn/Imakefile @@ -0,0 +1,26 @@ +# This file is part of libdyn.a, the C Dynamic Object library. It +# contains the Imakefile. +# +# There are no restrictions on this code; however, if you make any +# changes, I request that you document them so that I do not get +# credit or blame for your modifications. +# +# Written by Barr3y Jaspan, Student Information Processing Board (SIPB) +# and MIT-Project Athena, 1989. + +SRCS = dyn_create.c dyn_put.c dyn_debug.c dyn_delete.c dyn_size.c \ + dyn_append.c dyn_realloc.c dyn_paranoid.c dyn_insert.c \ + dyn_initzero.c +OBJS = dyn_create.o dyn_put.o dyn_debug.o dyn_delete.o dyn_size.o \ + dyn_append.o dyn_realloc.o dyn_paranoid.o dyn_insert.o \ + dyn_initzero.o +HDRS = dyn.h dynP.h + +DEST = libdyn.a + +StageLibrary($(DEST), $(OBJS)) +StageIncludes(dyn.h,) + +Program(test, test.o, $(DEST), $(DEST)) + +Depend(,, $(SRCS) $(HDRS)) diff --git a/src/util/dyn/Makefile.in b/src/util/dyn/Makefile.in new file mode 100644 index 000000000..e2c4255bd --- /dev/null +++ b/src/util/dyn/Makefile.in @@ -0,0 +1,65 @@ +CFLAGS = $(CCOPTS) $(DEFS) + +##DOSBUILDTOP = ..\.. +##DOSLIBNAME=libdyn.lib + +.c.o: + $(CC) $(CFLAGS) -c $(srcdir)/$*.c +@SHARED_RULE@ + +SRCS = $(srcdir)/dyn_create.c \ + $(srcdir)/dyn_put.c \ + $(srcdir)/dyn_debug.c \ + $(srcdir)/dyn_delete.c \ + $(srcdir)/dyn_size.c \ + $(srcdir)/dyn_append.c \ + $(srcdir)/dyn_realloc.c \ + $(srcdir)/dyn_paranoid.c \ + $(srcdir)/dyn_insert.c \ + $(srcdir)/dyn_initzero.c + +OBJS = dyn_create.$(OBJEXT) \ + dyn_put.$(OBJEXT) \ + dyn_debug.$(OBJEXT) \ + dyn_delete.$(OBJEXT) \ + dyn_size.$(OBJEXT) \ + dyn_append.$(OBJEXT) \ + dyn_realloc.$(OBJEXT) \ + dyn_paranoid.$(OBJEXT) \ + dyn_insert.$(OBJEXT) \ + dyn_initzero.$(OBJEXT) + +LIB_SUBDIRS= . +LIBDONE= DONE + +all-unix:: shared includes $(OBJS) +all-mac:: $(OBJS) +all-windows:: $(OBJS) + +shared: + mkdir shared + +check-windows:: + +clean-unix:: + $(RM) shared/* + +clean-mac:: +clean-windows:: + +DONE:: $(OBJS) + $(RM) DONE + echo $(OBJS) >DONE + +libdyn.$(STEXT): $(OBJS) + $(RM) $@ + $(ARADD) $@ $(OBJS) + $(RANLIB) $@ + +install:: libdyn.a + $(INSTALL_DATA) libdyn.a $(DESTDIR)$(KRB5_LIBDIR)/libdyn.a + $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libdyn.a + +clean:: + $(RM) libdyn.$(LIBEXT) libdyn.bak DONE + diff --git a/src/util/dyn/README b/src/util/dyn/README new file mode 100644 index 000000000..0c08ac5c6 --- /dev/null +++ b/src/util/dyn/README @@ -0,0 +1,32 @@ +libdyn.a -- Release 1.0 + +A C Dynamic Object is an array that takes care of resizing itself as +elements are added and deleted from it. It can be of any type for +which sizeof is defined and for which an address of a variable of that +type can be passed to a function. + +To build libdyn.a, simply type "make depend all" (if you don't have +the program makedepend, of course, leave out the "depend" part). If +your system's bcopy() cannot handle overlapping regions, you'll need +to write one that can. (Left as an excercise for the reader..) + +The library should compile and work without modification on a vast +number of systems. It only uses 5 external functions: malloc, +realloc, free, bcopy, and fprintf (to stderr). Of these, only bcopy +should need to be changed for other systems (such as MS-DOS) and it +could probably be done with a -D flag to the compiler. + +The test/demo program is built by "make all". This program produces +the library's debugging output (to stderr) as well as some of its own +output (to stdout). + +The library has been tested (with test.c) on a VAX VSII, VAXstation +3100, DECstation 3100, and IBM RT all running BSD4.3 (except for the +DECstation, which was running Ultrix V2.1). + +An earlier version of this library was posted to alt.sources. This +version contains one new function (DynInsert) and slightly cleaner +code, but no bugfixes (no bugs were found). + +Author: Barr3y Jaspan, Student Information Processing Board (SIPB) and +MIT-Project Athena, bjaspan@athena.mit.edu, 1990 diff --git a/src/util/dyn/TODO b/src/util/dyn/TODO new file mode 100644 index 000000000..d5a242b98 --- /dev/null +++ b/src/util/dyn/TODO @@ -0,0 +1,3 @@ +o be able to get obj->size +o be able to get array without DynAdd (so you can just use DynPut) +o be able to specify bzero on realloc diff --git a/src/util/dyn/configure.in b/src/util/dyn/configure.in new file mode 100644 index 000000000..6ba8e6d24 --- /dev/null +++ b/src/util/dyn/configure.in @@ -0,0 +1,14 @@ +AC_INIT(dyn.h) +CONFIG_RULES +AC_PROG_ARCHIVE +AC_PROG_ARCHIVE_ADD +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_CHECK_FUNCS(memmove) +V5_SHARED_LIB_OBJS +V5_MAKE_SHARED_LIB(libdyn,1.0,../../lib, ../util/dyn) +dnl DEPLIBS is normally set by KRB5_LIBRARIES, but that makes assumptions +dnl which fail for libdyn. +AC_SUBST(DEPLIBS) +CopySrcHeader(dyn.h,[$](BUILDTOP)/include) +V5_AC_OUTPUT_MAKEFILE diff --git a/src/util/dyn/dyn.3m b/src/util/dyn/dyn.3m new file mode 100644 index 000000000..21ae70bb2 --- /dev/null +++ b/src/util/dyn/dyn.3m @@ -0,0 +1,198 @@ + + + +15 March 1990 DYN(3M) + + + +NAME + dyn - the C Dynamic Object library + + +DESCRIPTION + A C Dynamic Object is an array that takes care of resizing + itself as you add and delete elements from it. It can be of + any type for which sizeof is defined and for which an + address of a variable of that type can be passed to a func- + tion. The library containing the functions described below + is called _l_i_b_d_y_n._a, and the necessary declarations to use + them are in <_d_y_n._h>. + + A DynObject is actually a structure that contains an array + and a couple of integers to maintain necessary state infor- + mation. When a Dyn function is said to operate on "the + object" or "the array", it is operating on the array stored + in the structure while at the same time updating internal + state information. + + +LIST OF FUNCTIONS + DynObject DynCreate(size, increment) + int size, increment; + + _R_e_q_u_i_r_e_s: _s_i_z_e and _i_n_c_r_e_m_e_n_t are greater than zero. + + _E_f_f_e_c_t_s: Creates a new DynObject that will store elements of + size _s_i_z_e and will allocate memory in blocks large enough to + hold exactly _i_n_c_r_e_m_e_n_t elements. For example, if you are + storing 8-byte double precision numbers and _i_n_c_r_e_m_e_n_t is 5, + each 5th element you add to the object will cause it to + request 40 more bytes (8 * 5) from the operating system. If + _i_n_c_r_e_m_e_n_t is zero, a default value is used (currently 100). + This is the only time the programmer deals with a dynamic + object's memory allocation. + + _R_e_t_u_r_n_s: DynCreate returns the new DynObject, or NULL if + there is insufficient memory. + + int DynDestroy(obj) + DynObject obj; + + _M_o_d_i_f_i_e_s: obj + + _E_f_f_e_c_t_s: Frees all memory associated with _o_b_j. The results + of calling any Dyn function on a destroyed object are unde- + fined (except for DynCreate, which resets the object). + + _R_e_t_u_r_n_s: DynDestroy returns DYN_OK. + + + + + + 1 + + + + + + +DYN(3M) 15 March 1990 + + + + int DynAdd(obj, el) + DynObject obj; + DynPtr el; + + _M_o_d_i_f_i_e_s: obj + + _E_f_f_e_c_t_s: Adds the element pointed to by _e_l to the object + _o_b_j, resizing the object if necessary. The new element + becomes the last element in obj's array. + + _R_e_t_u_r_n_s: DynAdd returns DYN_OK on success or DYN_NOMEM if + there is insufficient memory. + + int DynInsert(obj, index, els, num) + DynObject obj; + DynPtr els; + int index, num; + + _M_o_d_i_f_i_e_s: obj + + _E_f_f_e_c_t_s: Inserts the array of _n_u_m elements, pointed to by + _e_l_s, into the object _o_b_j starting at the array location + _i_n_d_e_x, resizing the object if necessary. Order is + preserved; if you have the array "1 2 3 4 5" and insert "10 + 11 12" at the third position, you will have the array "1 2 + 10 11 12 3 4 5". + + _R_e_t_u_r_n_s: DynInsert returns DYN_BADINDEX if _i_n_d_e_x is not + between 0 and DynSize(obj); DYN_BADVALUE if _n_u_m is less than + 1; DYN_NOMEM if there is insufficient memory. + + int DynGet(obj, index) + DynObject obj; + int index; + + _E_f_f_e_c_t_s: Returns the address of the element _i_n_d_e_x in the + array of _o_b_j. This pointer can be treated as a normal array + of the type specified to DynCreate. The order of elements + in this array is the order in which they were added to the + object. The returned pointer is guaranteed to be valid only + until obj is modified. + + _R_e_t_u_r_n_s: DynGet returns NULL if _i_n_d_e_x is larger than the + number of elements in the array of less than zero. + + int DynDelete(obj, index) + DynObject obj; + int index; + + _M_o_d_i_f_i_e_s: obj + + + + + +2 + + + + + + +15 March 1990 DYN(3M) + + + + _E_f_f_e_c_t_s: The element _i_n_d_e_x is deleted from the object _o_b_j. + Note that the element is actually removed permanently from + the array. If you have the array "1 2 3 4 5" and delete the + third element, you will have the array "1 2 4 5". The order + of elements in not affected. + + _R_e_t_u_r_n_s: DynDelete will return DYN_OK on success or + DYN_BADINDEX if the element _i_n_d_e_x does not exist in the + array or is less than zero. + + int DynSize(obj) + DynObject obj; + + _E_f_f_e_c_t_s: Returns the number of elements in the object _o_b_j. + + int DynHigh(obj) + DynObject obj; + + _E_f_f_e_c_t_s: Returns the index of the highest element in the + object _o_b_j. In this version, DynHigh is macro that expands + to DynSize - 1. + + int DynLow(obj) + DynObject obj; + + _E_f_f_e_c_t_s: Returns the index of the lowest element in the + object _o_b_j. In this version, DynLow is macro that expands + to 0. + + int DynDebug(obj, state) + DynObject obj; + int state; + + _M_o_d_i_f_i_e_s: obj + + _E_f_f_e_c_t_s: Sets the debugging state of _o_b_j to _s_t_a_t_e and prints + a message on stderr saying what state debugging was set to. + Any non-zero value for _s_t_a_t_e turns debugging ``on''. When + debugging is on, all Dyn functions will produce (hopefully + useful) output to stderr describing what is going on. + + _R_e_t_u_r_n_s: DynDebug returns DYN_OK. + +AUTHOR + Barr3y Jaspan, Student Information Processing Board (SIPB) + and MIT-Project Athena, bjaspan@athena.mit.edu + + + + + + + + + + 3 + + + diff --git a/src/util/dyn/dyn_append.c b/src/util/dyn/dyn_append.c new file mode 100644 index 000000000..81403ece7 --- /dev/null +++ b/src/util/dyn/dyn_append.c @@ -0,0 +1,26 @@ +/* + * This file is part of libdyn.a, the C Dynamic Object library. It + * contains the source code for the function DynAppend(). + * + * There are no restrictions on this code; however, if you make any + * changes, I request that you document them so that I do not get + * credit or blame for your modifications. + * + * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) + * and MIT-Project Athena, 1989. + */ + +#include + +#include "dynP.h" + +/* + * Made obsolete by DynInsert, now just a convenience function. + */ +int DynAppend(obj, els, num) + DynObjectP obj; + DynPtr els; + int num; +{ + return DynInsert(obj, DynSize(obj), els, num); +} diff --git a/src/util/dyn/dyn_initzero.c b/src/util/dyn/dyn_initzero.c new file mode 100644 index 000000000..3949f30ec --- /dev/null +++ b/src/util/dyn/dyn_initzero.c @@ -0,0 +1,26 @@ +/* + * This file is part of libdyn.a, the C Dynamic Object library. It + * contains the source code for the function DynInitZero(). + * + * There are no restrictions on this code; however, if you make any + * changes, I request that you document them so that I do not get + * credit or blame for your modifications. + * + * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) + * and MIT-Project Athena, 1989. + */ + +#include + +#include "dynP.h" + +int DynInitzero(obj, state) + DynObjectP obj; + int state; +{ + obj->initzero = state; + + if (obj->debug) + fprintf(stderr, "dyn: initzero: initzero set to %d.\n", state); + return DYN_OK; +} diff --git a/src/util/dyn/dyn_insert.c b/src/util/dyn/dyn_insert.c new file mode 100644 index 000000000..5654e935d --- /dev/null +++ b/src/util/dyn/dyn_insert.c @@ -0,0 +1,70 @@ +/* + * This file is part of libdyn.a, the C Dynamic Object library. It + * contains the source code for the function DynInsert(). + * + * There are no restrictions on this code; however, if you make any + * changes, I request that you document them so that I do not get + * credit or blame for your modifications. + * + * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) + * and MIT-Project Athena, 1989. + */ + +#include +#include "dynP.h" + +int DynInsert(obj, idx, els_in, num) + DynObjectP obj; + void *els_in; + int idx, num; +{ + DynPtr els = (DynPtr) els_in; + int ret; + + if (idx < 0 || idx > obj->num_el) { + if (obj->debug) + fprintf(stderr, "dyn: insert: index %d is not in [0,%d]\n", + idx, obj->num_el); + return DYN_BADINDEX; + } + + if (num < 1) { + if (obj->debug) + fprintf(stderr, "dyn: insert: cannot insert %d elements\n", + num); + return DYN_BADVALUE; + } + + if (obj->debug) + fprintf(stderr,"dyn: insert: Moving %d bytes from %d + %d to + %d\n", + (obj->num_el-idx)*obj->el_size, obj->array, + obj->el_size*idx, obj->el_size*(idx+num)); + + if ((ret = _DynResize(obj, obj->num_el + num)) != DYN_OK) + return ret; +#ifdef HAVE_MEMMOVE + memmove(obj->array + obj->el_size*(idx + num), + obj->array + obj->el_size*idx, + (obj->num_el-idx)*obj->el_size); +#else + bcopy(obj->array + obj->el_size*idx, + obj->array + obj->el_size*(idx + num), + (obj->num_el-idx)*obj->el_size); +#endif + + if (obj->debug) + fprintf(stderr, "dyn: insert: Copying %d bytes from %d to %d + %d\n", + obj->el_size*num, els, obj->array, obj->el_size*idx); + +#ifdef HAVE_MEMMOVE + memmove(obj->array + obj->el_size*idx, els, obj->el_size*num); +#else + bcopy(els, obj->array + obj->el_size*idx, obj->el_size*num); +#endif + obj->num_el += num; + + if (obj->debug) + fprintf(stderr, "dyn: insert: done.\n"); + + return DYN_OK; +} diff --git a/src/util/dyn/dyn_paranoid.c b/src/util/dyn/dyn_paranoid.c new file mode 100644 index 000000000..7eb750a21 --- /dev/null +++ b/src/util/dyn/dyn_paranoid.c @@ -0,0 +1,26 @@ +/* + * This file is part of libdyn.a, the C Dynamic Object library. It + * contains the source code for the function DynDebug(). + * + * There are no restrictions on this code; however, if you make any + * changes, I request that you document them so that I do not get + * credit or blame for your modifications. + * + * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) + * and MIT-Project Athena, 1989. + */ + +#include + +#include "dynP.h" + +int DynParanoid(obj, state) + DynObjectP obj; + int state; +{ + obj->paranoid = state; + + if (obj->debug) + fprintf(stderr, "dyn: paranoid: Paranoia set to %d.\n", state); + return DYN_OK; +} diff --git a/src/util/dyn/dyn_realloc.c b/src/util/dyn/dyn_realloc.c new file mode 100644 index 000000000..31e3975b5 --- /dev/null +++ b/src/util/dyn/dyn_realloc.c @@ -0,0 +1,88 @@ +/* + * This file is part of libdyn.a, the C Dynamic Object library. It + * contains the source code for the internal function _DynRealloc(). + * + * There are no restrictions on this code; however, if you make any + * changes, I request that you document them so that I do not get + * credit or blame for your modifications. + * + * Written by Barr3y Jaspan, Student Information Processing Board (SIPB) + * and MIT-Project Athena, 1989. + */ + +#include +#include + +#include "dynP.h" + +/* + * Resize the array so that element req exists. + */ +int _DynResize(obj, req) + DynObjectP obj; + int req; +{ + int cnt, size; + + if (obj->size > req) + return DYN_OK; + else if (obj->inc > 0) + return _DynRealloc(obj, (req - obj->size) / obj->inc + 1); + else { + if (obj->size == 0) + size = -obj->inc; + else + size = obj->size; + + while (size <= req) + size <<= 1; + + return _DynRealloc(obj, size); + } +} + +/* + * Resize the array by num_incs units. If obj->inc is positive, this + * means make it obj->inc*num_incs elements larger. If obj->inc is + * negative, this means make the array num_incs elements long. + * + * Ideally, this function should not be called from outside the + * library. However, nothing will break if it is. + */ +int _DynRealloc(obj, num_incs) + DynObjectP obj; + int num_incs; +{ + DynPtr temp; + int new_size_in_bytes; + + if (obj->inc > 0) + new_size_in_bytes = obj->el_size*(obj->size + obj->inc*num_incs); + else + new_size_in_bytes = obj->el_size*num_incs; + + if (obj->debug) + fprintf(stderr, + "dyn: alloc: Increasing object by %d bytes (%d incs).\n", + new_size_in_bytes - obj->el_size*obj->size, + num_incs); + + temp = (DynPtr) realloc(obj->array, new_size_in_bytes); + if (temp == NULL) { + if (obj->debug) + fprintf(stderr, "dyn: alloc: Out of memory.\n"); + return DYN_NOMEM; + } + else { + obj->array = temp; + if (obj->inc > 0) + obj->size += obj->inc*num_incs; + else + obj->size = num_incs; + } + + if (obj->debug) + fprintf(stderr, "dyn: alloc: done.\n"); + + return DYN_OK; +} diff --git a/src/util/et/ChangeLog b/src/util/et/ChangeLog index 34cc96338..233b5b9d7 100644 --- a/src/util/et/ChangeLog +++ b/src/util/et/ChangeLog @@ -14,6 +14,10 @@ Mon Jun 10 21:54:09 1996 Theodore Ts'o * vfprintf.c, internal.h, compile_et.c, et_c.awk, com_err.c: Change _WINDOWS to _MSDOS, and add check for _WIN32. +Sun May 12 01:13:02 1996 Marc Horowitz + + * et_c.awk: deal with continuations in the input .et file. + Wed Mar 20 00:19:08 1996 Theodore Y. Ts'o * Makefile.in (SRCS): Fix SRCS definition so that it doesn't fool diff --git a/src/util/et/et_c.awk b/src/util/et/et_c.awk index 8067e7190..ac1321cd0 100644 --- a/src/util/et/et_c.awk +++ b/src/util/et/et_c.awk @@ -120,7 +120,18 @@ c2n["_"]=63 table_item_count = 0 } +(continuation == 1) && ($0 ~ /\\[ \t]*$/) { + text=substr($0,1,length($0)-1); + printf "\t\t\"%s\"\n", text > outfile +} + +(continuation == 1) && ($0 ~ /"[ \t]*$/) { + printf "\t\t\"%s,\n", $0 > outfile + continuation = 0; +} + /^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*$/ { + table_item_count++ skipone=1 next } @@ -135,10 +146,28 @@ c2n["_"]=63 table_item_count++ } +/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*".*\\[ \t]*$/ { + text="" + for (i=3; i<=NF; i++) { + text = text FS $i + } + text=substr(text,2,length(text)-2); + printf "\t%s\"\n", text > outfile + continuation++; +} + +/^[ \t]*".*\\[ \t]*$/ { + if (skipone) { + text=substr($0,1,length($0)-1); + printf "\t%s\"\n", text > outfile + continuation++; + } + skipone=0 +} + { if (skipone) { printf "\t%s,\n", $0 > outfile - table_item_count++ } skipone=0 }