From: Luke Howard Date: Sun, 13 Sep 2009 07:49:54 +0000 (+0000) Subject: Merge trunk at 22736 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1626449fecf2b9ccbf0a47456c5267135077dc07;p=thirdparty%2Fkrb5.git Merge trunk at 22736 git-svn-id: svn://anonsvn.mit.edu/krb5/users/lhoward/authdata@22737 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/Makefile.in b/src/Makefile.in index bd67ad677c..d74e9e535c 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -195,7 +195,7 @@ WINMAKEFILES=Makefile \ include\Makefile \ lib\Makefile lib\crypto\Makefile \ lib\crypto\krb\crc32\Makefile lib\crypto\builtin\des\Makefile \ - lib\crypto\krb\dk\Makefile lib\crypto\krb\enc_provider\Makefile \ + lib\crypto\krb\dk\Makefile lib\crypto\builtin\enc_provider\Makefile \ lib\crypto\krb\hash_provider\Makefile \ lib\crypto\krb\keyhash_provider\Makefile \ lib\crypto\krb\raw\Makefile lib\crypto\old\Makefile \ @@ -268,7 +268,7 @@ WINMAKEFILES=Makefile \ ##DOS## $(WCONFIG) config < $@.in > $@ ##DOS##lib\crypto\krb\dk\Makefile: lib\crypto\krb\dk\Makefile.in $(MKFDEP) ##DOS## $(WCONFIG) config < $@.in > $@ -##DOS##lib\crypto\krb\enc_provider\Makefile: lib\crypto\krb\enc_provider\Makefile.in $(MKFDEP) +##DOS##lib\crypto\builtin\enc_provider\Makefile: lib\crypto\builtin\enc_provider\Makefile.in $(MKFDEP) ##DOS## $(WCONFIG) config < $@.in > $@ ##DOS##lib\crypto\krb\hash_provider\Makefile: lib\crypto\krb\hash_provider\Makefile.in $(MKFDEP) ##DOS## $(WCONFIG) config < $@.in > $@ @@ -395,7 +395,7 @@ FILES= ./* \ config/* include/* include/kerberosIV/* \ include/krb5/* include/krb5/stock/* include/sys/* lib/* \ lib/crypto/* lib/crypto/krb/crc32/* lib/crypto/builtin/des/* lib/crypto/krb/dk/* \ - lib/crypto/krb/enc_provider/* lib/crypto/krb/hash_provider/* \ + lib/crypto/builtin/enc_provider/* lib/crypto/krb/hash_provider/* \ lib/crypto/krb/keyhash_provider/* lib/crypto/krb/old/* lib/crypto/krb/raw/* \ lib/crypto/builtin/sha1/* lib/crypto/builtin/arcfour/* lib/crypto/builtin/md4/* \ lib/crypto/builtin/md5/* lib/crypto/krb/yarrow/* \ diff --git a/src/clients/kinit/kinit.M b/src/clients/kinit/kinit.M index fb5a47a251..5b85772ac2 100644 --- a/src/clients/kinit/kinit.M +++ b/src/clients/kinit/kinit.M @@ -35,9 +35,11 @@ kinit \- obtain and cache Kerberos ticket-granting ticket [\fB\-f\fP | \fB\-F\fP] [\fB\-a\fP] [\fB\-A\fP] +[\fB\-C\fP] +[\fB\-E\fP] [\fB\-v\fP] [\fB\-R\fP] [\fB\-k\fP [\fB\-t\fP \fIkeytab_file\fP]] [\fB\-c\fP \fIcache_name\fP] -[\fB\-S\fP \fIservice_name\fP][\fB\-T\fP \fIarmor_ccache\fP] +[\fB\-S\fP \fIservice_name\fP][\fB\-T\fP \fIarmor_ccache\fP] [\fB\-X\fP \fIattribute\fP[=\fIvalue\fP]] [\fIprincipal\fP] .ad b @@ -109,6 +111,12 @@ request tickets with the local address[es]. .B \-A request address-less tickets. .TP +.B \-C +requests canonicalization of the principal name. +.TP +.B \-E +treats the principal name as an enterprise name. +.TP .B \-v requests that the ticket granting ticket in the cache (with the .I invalid diff --git a/src/clients/kpasswd/ksetpwd.c b/src/clients/kpasswd/ksetpwd.c index 45f782f08f..a489f06e3d 100644 --- a/src/clients/kpasswd/ksetpwd.c +++ b/src/clients/kpasswd/ksetpwd.c @@ -282,8 +282,6 @@ int main( int argc, char ** argv ) /* ** change the password - */ - fprintf( stderr, "the password is %s\n", new_password ); - { int pw_result; krb5_ccache ccache; diff --git a/src/clients/kvno/kvno.M b/src/clients/kvno/kvno.M index b7e4d46a0d..37b0bcbd53 100644 --- a/src/clients/kvno/kvno.M +++ b/src/clients/kvno/kvno.M @@ -51,6 +51,13 @@ suppress printing .B \-h prints a usage statement and exits .TP +.B \-P +specifies that the +.B service1 service2 ... +arguments are to be treated as services for which credentials should +be acquired using constrained delegation. This option is only valid +when used in conjunction with protocol transition. +.TP .B \-S sname specifies that krb5_sname_to_principal() will be used to build principal names. If this flag is specified, the @@ -59,6 +66,13 @@ arguments are interpreted as hostnames (rather than principal names), and .B sname is interpreted as the service name. +.TP +.B \-U for_user +specifies that protocol transition (S4U2Self) is to be used to acquire +a ticket on behalf of +.B for_user. +If constrained delegation is not requested, the service name +must match the credentials cache client principal. .SH ENVIRONMENT .B Kvno uses the following environment variable: diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c index b98b85d304..58702525f6 100644 --- a/src/clients/kvno/kvno.c +++ b/src/clients/kvno/kvno.c @@ -39,8 +39,9 @@ static char *prog; static void xusage() { - fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype] [-k keytab] [-S sname] service1 service2 ...\n", - prog); + fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype]\n", prog); + fprintf(stderr, "\t[-k keytab] [-S sname] [-U for_user [-P]]\n"); + fprintf(stderr, "\tservice1 service2 ...\n"); exit(1); } @@ -48,7 +49,8 @@ int quiet = 0; static void do_v5_kvno (int argc, char *argv[], char *ccachestr, char *etypestr, char *keytab_name, - char *sname, int canon, int unknown); + char *sname, int canon, int unknown, + char *for_user, int proxy); #include static void extended_com_err_fn (const char *, errcode_t, const char *, @@ -58,8 +60,8 @@ int main(int argc, char *argv[]) { int option; char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL; - char *sname = NULL; - int canon = 0, unknown = 0; + char *sname = NULL, *for_user = NULL; + int canon = 0, unknown = 0, proxy = 0; set_com_err_hook (extended_com_err_fn); @@ -67,7 +69,7 @@ int main(int argc, char *argv[]) prog = strrchr(argv[0], '/'); prog = prog ? (prog + 1) : argv[0]; - while ((option = getopt(argc, argv, "uCc:e:hk:qS:")) != -1) { + while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) { switch (option) { case 'C': canon = 1; @@ -87,6 +89,9 @@ int main(int argc, char *argv[]) case 'q': quiet = 1; break; + case 'P': + proxy = 1; /* S4U2Proxy - constrained delegation */ + break; case 'S': sname = optarg; if (unknown == 1){ @@ -101,21 +106,37 @@ int main(int argc, char *argv[]) xusage(); } break; + case 'U': + for_user = optarg; /* S4U2Self - protocol transition */ + break; default: xusage(); break; } } + if (proxy) { + if (keytab_name == NULL) { + fprintf(stderr, "Option -P (constrained delegation) " + "requires keytab to be specified\n"); + xusage(); + } else if (for_user == NULL) { + fprintf(stderr, "Option -P (constrained delegation) requires " + "option -U (protocol transition)\n"); + xusage(); + } + } + if ((argc - optind) < 1) xusage(); do_v5_kvno(argc - optind, argv + optind, - ccachestr, etypestr, keytab_name, sname, canon, unknown); + ccachestr, etypestr, keytab_name, sname, + canon, unknown, for_user, proxy); return 0; } -#include +#include static krb5_context context; static void extended_com_err_fn (const char *myprog, errcode_t code, const char *fmt, va_list args) @@ -130,17 +151,18 @@ static void extended_com_err_fn (const char *myprog, errcode_t code, static void do_v5_kvno (int count, char *names[], char * ccachestr, char *etypestr, char *keytab_name, - char *sname, int canon, int unknown) + char *sname, int canon, int unknown, char *for_user, + int proxy) { krb5_error_code ret; int i, errors; krb5_enctype etype; krb5_ccache ccache; krb5_principal me; - krb5_creds in_creds, *out_creds; - krb5_ticket *ticket; - char *princ; + krb5_creds in_creds; krb5_keytab keytab = NULL; + krb5_principal for_user_princ = NULL; + krb5_flags options; ret = krb5_init_context(&context); if (ret) { @@ -175,6 +197,16 @@ static void do_v5_kvno (int count, char *names[], } } + if (for_user) { + ret = krb5_parse_name_flags(context, for_user, + KRB5_PRINCIPAL_PARSE_ENTERPRISE, + &for_user_princ); + if (ret) { + com_err(prog, ret, "while parsing principal name %s", for_user); + exit(1); + } + } + ret = krb5_cc_get_principal(context, ccache, &me); if (ret) { com_err(prog, ret, "while getting client principal name"); @@ -183,91 +215,131 @@ static void do_v5_kvno (int count, char *names[], errors = 0; + options = 0; + if (canon) + options |= KRB5_GC_CANONICALIZE; + for (i = 0; i < count; i++) { - memset(&in_creds, 0, sizeof(in_creds)); + krb5_principal server = NULL; + krb5_ticket *ticket = NULL; + krb5_creds *out_creds = NULL; + char *princ = NULL; - in_creds.client = me; + memset(&in_creds, 0, sizeof(in_creds)); if (sname != NULL) { ret = krb5_sname_to_principal(context, names[i], sname, KRB5_NT_SRV_HST, - &in_creds.server); + &server); } else { - ret = krb5_parse_name(context, names[i], &in_creds.server); + ret = krb5_parse_name(context, names[i], &server); } if (ret) { if (!quiet) com_err(prog, ret, "while parsing principal name %s", names[i]); - errors++; - continue; + goto error; } if (unknown == 1) { - krb5_princ_type(context, in_creds.server) = KRB5_NT_UNKNOWN; + krb5_princ_type(context, server) = KRB5_NT_UNKNOWN; } - ret = krb5_unparse_name(context, in_creds.server, &princ); + ret = krb5_unparse_name(context, server, &princ); if (ret) { com_err(prog, ret, "while formatting parsed principal name for '%s'", names[i]); - errors++; - continue; + goto error; } in_creds.keyblock.enctype = etype; - ret = krb5_get_credentials(context, canon ? KRB5_GC_CANONICALIZE : 0, - ccache, &in_creds, &out_creds); + if (for_user) { + if (!proxy && + !krb5_principal_compare(context, me, server)) { + com_err(prog, EINVAL, + "client and server principal names must match"); + goto error; + } - krb5_free_principal(context, in_creds.server); + in_creds.client = for_user_princ; + in_creds.server = me; + + ret = krb5_get_credentials_for_user(context, options, ccache, + &in_creds, NULL, &out_creds); + } else { + in_creds.client = me; + in_creds.server = server; + ret = krb5_get_credentials(context, options, ccache, + &in_creds, &out_creds); + } if (ret) { com_err(prog, ret, "while getting credentials for %s", princ); - - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } /* we need a native ticket */ ret = krb5_decode_ticket(&out_creds->ticket, &ticket); if (ret) { com_err(prog, ret, "while decoding ticket for %s", princ); - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } - + if (keytab) { ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket); if (ret) { - if (!quiet) - printf("%s: kvno = %d, keytab entry invalid", princ, ticket->enc_part.kvno); + if (!quiet) { + fprintf(stderr, "%s: kvno = %d, keytab entry invalid\n", + princ, ticket->enc_part.kvno); + } com_err(prog, ret, "while decrypting ticket for %s", princ); - krb5_free_ticket(context, ticket); - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } if (!quiet) - printf("%s: kvno = %d, keytab entry valid\n", princ, ticket->enc_part.kvno); + printf("%s: kvno = %d, keytab entry valid\n", + princ, ticket->enc_part.kvno); + if (proxy) { + krb5_free_creds(context, out_creds); + out_creds = NULL; + + in_creds.client = ticket->enc_part2->client; + in_creds.server = server; + + ret = krb5_get_credentials_for_proxy(context, + KRB5_GC_CANONICALIZE, + ccache, + &in_creds, + ticket, + &out_creds); + if (ret) { + com_err(prog, ret, + "%s: constrained delegation failed", princ); + goto error; + } + } } else { if (!quiet) printf("%s: kvno = %d\n", princ, ticket->enc_part.kvno); } - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); + continue; + +error: + if (server != NULL) + krb5_free_principal(context, server); + if (ticket != NULL) + krb5_free_ticket(context, ticket); + if (out_creds != NULL) + krb5_free_creds(context, out_creds); + if (princ != NULL) + krb5_free_unparsed_name(context, princ); + errors++; } if (keytab) krb5_kt_close(context, keytab); krb5_free_principal(context, me); + krb5_free_principal(context, for_user_princ); krb5_cc_close(context, ccache); krb5_free_context(context); diff --git a/src/configure.in b/src/configure.in index 122d06abe8..6462e5272f 100644 --- a/src/configure.in +++ b/src/configure.in @@ -1,19 +1,19 @@ K5_AC_INIT([aclocal.m4]) -dnl -dnl autoconf 2.49 defaults to a /dev/null cache file, which is what we -dnl do not want for performance reasons. +# +# autoconf 2.49 defaults to a /dev/null cache file, which is what we +# do not want for performance reasons. if test "x$cache_file" = "x/dev/null"; then cache_file=./config.cache AC_CACHE_LOAD fi -dnl + CONFIG_RULES KRB5_VERSION=K5_VERSION AC_SUBST(KRB5_VERSION) -dnl -dnl + + AC_REQUIRE_CPP -dnl + AC_CACHE_CHECK(if va_copy is available, krb5_cv_va_copy, [AC_LINK_IFELSE([ #include @@ -31,10 +31,10 @@ int main() if test "$krb5_cv_va_copy" = yes; then AC_DEFINE(HAS_VA_COPY,1,[Define if va_copy macro or function is available.]) fi -dnl -dnl Note that this isn't checking if the copied value *works*, just -dnl whether the C language constraints permit the copying. If -dnl va_list is defined as an array type, it can't be assigned. + +# Note that this isn't checking if the copied value *works*, just +# whether the C language constraints permit the copying. If +# va_list is defined as an array type, it can't be assigned. AC_CACHE_CHECK(if va_list objects can be copied by assignment, krb5_cv_va_simple_copy, [AC_COMPILE_IFELSE([ @@ -46,23 +46,23 @@ void f(va_list va2) { if test "$krb5_cv_va_simple_copy" = yes; then AC_DEFINE(CAN_COPY_VA_LIST,1,[Define if va_list objects can be simply copied by assignment.]) fi -dnl -dnl The following lines are so that configure --help gives some global -dnl configuration options. -dnl + +# The following lines are so that configure --help gives some global +# configuration options. + KRB5_LIB_AUX AC_KRB5_TCL AC_ARG_ENABLE([athena], [ --enable-athena build with MIT Project Athena configuration],,) -dnl -dnl Begin autoconf tests for the Makefiles generated out of the top-level -dnl configure.in... -dnl + +# Begin autoconf tests for the Makefiles generated out of the top-level +# configure.in... + AC_CHECK_FUNCS(memmove) KRB5_BUILD_LIBOBJS KRB5_BUILD_LIBRARY KRB5_BUILD_PROGRAM -dnl for slave +# for slave AC_TYPE_MODE_T AC_PROG_INSTALL KRB5_AC_NEED_DAEMON @@ -73,7 +73,7 @@ AC_CHECK_LIB(util,main,[AC_DEFINE(HAVE_LIBUTIL,1,[Define if the util library is LIBUTIL=-lutil ]) AC_SUBST(LIBUTIL) -dnl for kdc +# for kdc AC_CHECK_HEADERS(syslog.h stdarg.h sys/select.h sys/sockio.h ifaddrs.h unistd.h) AC_CHECK_FUNCS(openlog syslog closelog strftime vsprintf vasprintf vsnprintf) AC_CHECK_FUNCS(strlcpy) @@ -100,68 +100,68 @@ KRB5_NEED_PROTO([#include #include ],swab,1) KRB5_NEED_PROTO([#include ],isblank,1) -dnl + AC_PROG_AWK KRB5_AC_INET6 KRB5_SOCKADDR_SA_LEN CHECK_SIGNALS -dnl -dnl --with-vague-errors disables useful error messages. -dnl + +# --with-vague-errors disables useful error messages. + AC_ARG_WITH([vague-errors], -AC_HELP_STRING([--with-vague-errors],[Do not @<:@do@:>@ send helpful errors to client]), , withval=no)dnl +AC_HELP_STRING([--with-vague-errors],[Do not @<:@do@:>@ send helpful errors to client]), , withval=no) if test "$withval" = yes; then AC_MSG_RESULT(Supplying vague error messages to KDC clients) AC_DEFINE(KRBCONF_VAGUE_ERRORS,1,[Define if the KDC should return only vague error codes to clients]) fi -dnl -dnl WITH_CRYPTO_IMPL -dnl + +# WITH_CRYPTO_IMPL + CRYPTO_IMPL="builtin" AC_ARG_WITH([crypto-impl], AC_HELP_STRING([--with-crypto-impl=IMPL], [use specified crypto implementation @<:@builtin@:>@]), [CRYPTO_IMPL=$withval AC_MSG_RESULT("k5crypto will use \'$withval\'") -], withval=builtin)dnl +], withval=builtin) AC_SUBST(CRYPTO_IMPL) -dnl -dnl --with-kdc-kdb-update makes the KDC update the database with last request -dnl information and failure information. -dnl + +# --with-kdc-kdb-update makes the KDC update the database with last request +# information and failure information. + AC_ARG_WITH([kdc-kdb-update], -AC_HELP_STRING([--with-kdc-kdb-update],[Update the database @<:@don't update@:>@]), , withval=no)dnl +AC_HELP_STRING([--with-kdc-kdb-update],[Update the database @<:@don't update@:>@]), , withval=no) if test "$withval" = yes; then AC_MSG_RESULT(Updating KDC database with each request) AC_DEFINE(KRBCONF_KDC_MODIFIES_KDB,1,[Define if KDC should update database with each request]) fi -dnl -dnl Needed for hw-preauth replay detection on KDC. -dnl -dnl USE_RCACHE enables the replay cache -dnl NOCACHE disables the lookaside cache -dnl -dnl The lookaside cache is checked first; if *exactly* the same message -dnl comes in twice, e.g., because the (legitimate) client resent it, -dnl the previous response will be resent. Otherwise, the replay cache -dnl is used to check for attempts to fake out the KDC. Some hardware -dnl preauth methods are weak enough that we *really* want to have this -dnl checking turned on. -dnl + +# Needed for hw-preauth replay detection on KDC. + +# USE_RCACHE enables the replay cache +# NOCACHE disables the lookaside cache + +# The lookaside cache is checked first; if *exactly* the same message +# comes in twice, e.g., because the (legitimate) client resent it, +# the previous response will be resent. Otherwise, the replay cache +# is used to check for attempts to fake out the KDC. Some hardware +# preauth methods are weak enough that we *really* want to have this +# checking turned on. + AC_ARG_ENABLE([kdc-replay-cache], -AC_HELP_STRING([--enable-kdc-replay-cache],[check for replayed/retransmitted KDC requests (recommended when hardware preauthentication is in use) @<:@disabled@:>@]), , enableval=yes)dnl +AC_HELP_STRING([--enable-kdc-replay-cache],[check for replayed/retransmitted KDC requests (recommended when hardware preauthentication is in use) @<:@disabled@:>@]), , enableval=yes) if test "$enableval" = yes ; then AC_DEFINE(USE_RCACHE,1,[Define if the KDC should use a replay cache]) else AC_DEFINE(NOCACHE,1,[Define if the KDC should use no replay cache]) fi KRB5_RUN_FLAGS -dnl + AC_TYPE_SIGNAL -dnl -dnl from old include/configure.in + +# from old include/configure.in AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_STORAGE], [Define if "struct sockaddr_storage" is available.]) -dnl + AC_CONFIG_HEADERS(include/autoconf.h, [echo timestamp > include/autoconf.stamp]) AC_PROG_INSTALL AC_PROG_AWK @@ -169,7 +169,7 @@ AC_PROG_LEX AC_C_CONST AC_HEADER_DIRENT AC_CHECK_FUNCS(strdup setvbuf inet_ntoa inet_aton seteuid setresuid setreuid setegid setresgid setregid setsid flock fchmod chmod strftime strptime geteuid setenv unsetenv getenv gethostbyname2 getifaddrs gmtime_r localtime_r pthread_mutex_lock sched_yield bswap16 bswap64 mkstemp getusershell lstat access ftime getcwd srand48 srand srandom stat strchr strerror strerror_r strstr timezone umask waitpid sem_init sem_trywait daemon) -dnl + AC_CHECK_FUNC(mkstemp, [MKSTEMP_ST_OBJ= MKSTEMP_OBJ=], @@ -179,7 +179,7 @@ EXTRA_SUPPORT_SYMS="$EXTRA_SUPPORT_SYMS krb5int_mkstemp"]) AC_SUBST(MKSTEMP_OBJ) AC_SUBST(MKSTEMP_ST_OBJ) AC_SUBST(EXTRA_SUPPORT_SYMS) -dnl + AC_HEADER_STDARG DECLARE_SYS_ERRLIST AC_CHECK_HEADERS(unistd.h paths.h regex.h regexpr.h fcntl.h memory.h ifaddrs.h sys/filio.h sched.h byteswap.h machine/endian.h machine/byte_order.h sys/bswap.h endian.h pwd.h arpa/inet.h alloca.h dlfcn.h limits.h pthread.h semaphore.h krb_db.h kdc.h) @@ -198,25 +198,25 @@ AC_CHECK_MEMBERS([struct stat.st_mtimensec,struct stat.st_mtimespec.tv_nsec,stru #include ]) KRB5_AC_REGEX_FUNCS AC_TYPE_OFF_T -dnl -dnl Fancy caching of perror result... + +# Fancy caching of perror result... AC_MSG_CHECKING(for perror declaration) AC_CACHE_VAL(krb5_cv_decl_perror, [AC_EGREP_HEADER(perror, errno.h, - krb5_cv_decl_perror=yes, krb5_cv_decl_perror=no)])dnl + krb5_cv_decl_perror=yes, krb5_cv_decl_perror=no)]) AC_MSG_RESULT($krb5_cv_decl_perror) if test $krb5_cv_decl_perror = yes; then AC_DEFINE(HDR_HAS_PERROR,1,[Define if errno.h declares perror]) fi -dnl + KRB5_NEED_PROTO([#include ],strptime) CHECK_WAIT_TYPE CHECK_SIGPROCMASK AC_TYPE_GETGROUPS CHECK_SETJMP -dnl -dnl *rpcent return types needed for lib/rpc -dnl + +# *rpcent return types needed for lib/rpc + AC_MSG_CHECKING([return type of setrpcent]) AC_CACHE_VAL(k5_cv_type_setrpcent, [AC_TRY_COMPILE([#include @@ -224,10 +224,10 @@ AC_CACHE_VAL(k5_cv_type_setrpcent, extern "C" #endif extern void setrpcent();], -[int i;], k5_cv_type_setrpcent=void, k5_cv_type_setrpcent=int)])dnl +[int i;], k5_cv_type_setrpcent=void, k5_cv_type_setrpcent=int)]) AC_MSG_RESULT($k5_cv_type_setrpcent) AC_DEFINE_UNQUOTED(SETRPCENT_TYPE, $k5_cv_type_setrpcent, [Define as return type of setrpcent]) -dnl + AC_MSG_CHECKING([return type of endrpcent]) AC_CACHE_VAL(k5_cv_type_endrpcent, [AC_TRY_COMPILE([#include @@ -235,12 +235,12 @@ AC_CACHE_VAL(k5_cv_type_endrpcent, extern "C" #endif extern void endrpcent();], -[int i;], k5_cv_type_endrpcent=void, k5_cv_type_endrpcent=int)])dnl +[int i;], k5_cv_type_endrpcent=void, k5_cv_type_endrpcent=int)]) AC_MSG_RESULT($k5_cv_type_endrpcent) AC_DEFINE_UNQUOTED(ENDRPCENT_TYPE, $k5_cv_type_endrpcent, [Define as return type of endrpcent]) -dnl -dnl -dnl bswap_16 is a macro in byteswap.h under GNU libc + + +# bswap_16 is a macro in byteswap.h under GNU libc AC_MSG_CHECKING(for bswap_16) AC_CACHE_VAL(krb5_cv_bswap_16,[ AC_TRY_LINK([#if HAVE_BYTESWAP_H @@ -259,9 +259,9 @@ AC_MSG_RESULT($krb5_cv_bswap_64) if test "$krb5_cv_bswap_64" = yes; then AC_DEFINE(HAVE_BSWAP_64,1,[Define to 1 if bswap_64 is available via byteswap.h]) fi -dnl -dnl Needed for ksu and some appl stuff. -dnl + +# Needed for ksu and some appl stuff. + case $krb5_cv_host in alpha*-dec-osf*) AC_CHECK_LIB(security,setluid, @@ -271,7 +271,7 @@ alpha*-dec-osf*) ;; esac AC_SUBST(KSU_LIBS) -dnl + if test $ac_cv_func_setenv = no || test $ac_cv_func_unsetenv = no \ || test $ac_cv_func_getenv = no; then SETENVOBJ=setenv.o @@ -279,9 +279,9 @@ else SETENVOBJ= fi AC_SUBST(SETENVOBJ) -dnl -dnl Check what the return types for gethostbyname_r and getservbyname_r are. -dnl + +# Check what the return types for gethostbyname_r and getservbyname_r are. + AC_CHECK_FUNC(gethostbyname_r,[ ac_cv_func_gethostbyname_r=yes if test "$ac_cv_func_gethostbyname_r" = yes; then @@ -314,7 +314,7 @@ if test "$ac_cv_func_gethostbyname_r" = yes; then AC_CHECK_FUNC(gethostbyaddr_r) fi ]) -dnl + AC_CHECK_FUNC(getpwnam_r,ac_cv_func_getpwnam_r=yes,ac_cv_func_getpwnam_r=no) AC_CHECK_FUNC(getpwuid_r,ac_cv_func_getpwuid_r=yes,ac_cv_func_getpwuid_r=no) @@ -436,23 +436,23 @@ if test "$ac_cv_func_getservbyname_r" = yes; then AC_CHECK_FUNC(getservbyport_r) fi ]) -dnl + HAVE_YYLINENO CHECK_DIRENT AC_TYPE_UID_T AC_TYPE_MODE_T -dnl -AC_CHECK_HEADER(termios.h,dnl -[AC_CHECK_FUNC([tcsetattr],dnl + +AC_CHECK_HEADER(termios.h, +[AC_CHECK_FUNC([tcsetattr], AC_DEFINE(POSIX_TERMIOS,1,[Define if termios.h exists and tcsetattr exists]))]) -dnl + KRB5_SIGTYPE AC_CHECK_HEADERS(stdlib.h string.h stddef.h sys/types.h sys/file.h sys/param.h sys/stat.h sys/time.h netinet/in.h sys/uio.h sys/filio.h sys/select.h time.h paths.h errno.h) AC_HEADER_STDARG KRB5_AC_INET6 -dnl -dnl If compiling with IPv6 support, test if in6addr_any functions. -dnl Irix 6.5.16 defines it, but lacks support in the C library. + +# If compiling with IPv6 support, test if in6addr_any functions. +# Irix 6.5.16 defines it, but lacks support in the C library. if test $krb5_cv_inet6 = yes || test "$krb5_cv_inet6_with_dinet6" = yes ; then AC_CACHE_CHECK([for in6addr_any definition in library], krb5_cv_var_in6addr_any, @@ -473,11 +473,11 @@ AC_CACHE_CHECK([for in6addr_any definition in library], fi fi -dnl -dnl -dnl check for ANSI stdio, esp "b" option to fopen(). This (unfortunately) -dnl requires a run check... -dnl + + +# check for ANSI stdio, esp "b" option to fopen(). This (unfortunately) +# requires a run check... + AC_MSG_CHECKING([for ANSI stdio]) AC_CACHE_VAL(krb5_cv_has_ansi_stdio, [AC_TRY_RUN( @@ -492,19 +492,19 @@ int main() exit(0); }], krb5_cv_has_ansi_stdio=yes, krb5_cv_has_ansi_stdio=no, -krb5_cv_has_ansi_stdio=yes)])dnl assume ANSI in cross environment +krb5_cv_has_ansi_stdio=yes)])# assume ANSI in cross environment AC_MSG_RESULT($krb5_cv_has_ansi_stdio) if test $krb5_cv_has_ansi_stdio = yes; then AC_DEFINE(ANSI_STDIO,1,[Define if ANSI stdio is present (in particular "b" option to fopen)]) fi -dnl -dnl then from osconf.h, we have -dnl + +# then from osconf.h, we have + AC_HEADER_TIME AC_CHECK_TYPE(time_t, long) -dnl -dnl Determine where to put the replay cache. -dnl + +# Determine where to put the replay cache. + AC_MSG_CHECKING([for replay cache directory]) AC_CACHE_VAL(krb5_cv_sys_rcdir, [ @@ -512,12 +512,12 @@ for t_dir in /var/tmp /usr/tmp /var/usr/tmp /tmp ; do test -d $t_dir || continue krb5_cv_sys_rcdir=$t_dir break -done])dnl +done]) AC_MSG_RESULT($krb5_cv_sys_rcdir) KRB5_RCTMPDIR=$krb5_cv_sys_rcdir AC_SUBST(KRB5_RCTMPDIR) -dnl -dnl + + AC_MSG_CHECKING(for socklen_t) AC_CACHE_VAL(krb5_cv_has_type_socklen_t, [AC_TRY_COMPILE( @@ -529,7 +529,7 @@ AC_MSG_RESULT($krb5_cv_has_type_socklen_t) if test $krb5_cv_has_type_socklen_t = yes; then AC_DEFINE(HAVE_SOCKLEN_T,1,[Define if there is a socklen_t type. If not, probably use size_t]) fi -dnl + AC_MSG_CHECKING(for struct lifconf) AC_CACHE_VAL(krb5_cv_has_struct_lifconf, [AC_TRY_COMPILE( @@ -541,7 +541,7 @@ AC_MSG_RESULT($krb5_cv_has_struct_lifconf) if test $krb5_cv_has_struct_lifconf = yes; then AC_DEFINE(HAVE_STRUCT_LIFCONF,1,[Define if there is a struct lifconf.]) fi -dnl HP-UX 11 uses stuct if_laddrconf +# HP-UX 11 uses stuct if_laddrconf AC_MSG_CHECKING(for struct if_laddrconf) AC_CACHE_VAL(krb5_cv_has_struct_if_laddrconf, [AC_TRY_COMPILE( @@ -554,8 +554,8 @@ AC_MSG_RESULT($krb5_cv_has_struct_if_laddrconf) if test $krb5_cv_has_struct_if_laddrconf = yes; then AC_DEFINE(HAVE_STRUCT_IF_LADDRCONF,1,[Define if there is a struct if_laddrconf.]) fi -dnl -dnl + + AC_MSG_CHECKING([for h_errno in netdb.h]) AC_CACHE_VAL(krb5_cv_header_netdb_h_h_errno, [AC_TRY_COMPILE( @@ -567,13 +567,13 @@ if test $krb5_cv_header_netdb_h_h_errno = yes; then AC_DEFINE([HAVE_NETDB_H_H_ERRNO], 1, [Define if netdb.h declares h_errno]) fi -dnl -dnl + + AC_ARG_ENABLE([athena], [ --enable-athena build with MIT Project Athena configuration], AC_DEFINE(KRB5_ATHENA_COMPAT,1,[Define if MIT Project Athena default configuration should be used]),) -dnl + AC_C_INLINE AH_TOP([ #ifndef KRB5_AUTOCONF_H @@ -586,9 +586,9 @@ AH_BOTTOM([ #endif #endif /* KRB5_AUTOCONF_H */ ]) -dnl -dnl Not used yet, but let's find out what we've got on the platforms -dnl we're working with.... + +# Not used yet, but let's find out what we've got on the platforms +# we're working with.... AC_CHECK_HEADERS(inttypes.h stdint.h) AC_CHECK_TYPES([uint32_t, int32_t, uint64_t, int64_t, uint_least32_t, uintptr_t, uintmax_t, long long], , , [ #ifdef HAVE_STDINT_H @@ -607,19 +607,19 @@ AC_CHECK_TYPES([struct rt_msghdr], , , [ #include #include ]) -dnl -dnl stuff for util/profile -dnl -dnl AC_KRB5_TCL already done + +# stuff for util/profile + +# AC_KRB5_TCL already done DO_TCL= test "$TCL_LIBS" != "" && DO_TCL=ok AC_SUBST(DO_TCL) -dnl -dnl types libdb2 wants -dnl + +# types libdb2 wants + AC_CHECK_TYPES([ssize_t, u_char, u_int, u_long, u_int8_t, u_int16_t, u_int32_t, int8_t, int16_t, int32_t]) -dnl -dnl Some libdb2 test programs want a shell that supports functions. + +# Some libdb2 test programs want a shell that supports functions. FCTSH=false AC_PATH_PROG(SH,sh,false) AC_PATH_PROG(SH5,sh5,false) @@ -635,10 +635,10 @@ for prog in $SH $SH5 $BASH; do fi done AC_SUBST(FCTSH) -dnl -dnl Test for POSIX 2001 *printf support (X/Open System Interfaces extension -dnl to ANSI/ISO C 1999 specification). Specifically, positional -dnl specifications; not checking for other features like %zx at present. + +# Test for POSIX 2001 *printf support (X/Open System Interfaces extension +# to ANSI/ISO C 1999 specification). Specifically, positional +# specifications; not checking for other features like %zx at present. AC_MSG_CHECKING(for POSIX printf positional specification support) AC_CACHE_VAL(ac_cv_printf_positional,[ AC_TRY_RUN([ @@ -657,19 +657,19 @@ int main () { ac_cv_printf_positional=yes, ac_cv_printf_positional=no, AC_MSG_ERROR([Cannot test for printf positional argument support when cross compiling]))]) -dnl Nothing for autoconf.h for now. +# Nothing for autoconf.h for now. AC_MSG_RESULT($ac_cv_printf_positional) -dnl -dnl -dnl for kadmin -dnl + + +# for kadmin + AC_PROG_YACC ath_compat= AC_ARG_ENABLE([athena], [ --enable-athena build with MIT Project Athena configuration], ath_compat=compat,) -dnl The following are tests for the presence of programs required for -dnl kadmin testing. +# The following are tests for the presence of programs required for +# kadmin testing. AC_CHECK_PROG(have_RUNTEST,runtest,runtest) AC_CHECK_PROG(have_PERL,perl,perl) AC_KRB5_TCL @@ -677,8 +677,8 @@ if test "$have_PERL" = perl -a "$have_RUNTEST" = runtest -a "$TCL_LIBS" != ""; t DO_TEST=ok fi AC_SUBST(DO_TEST) -dnl -dnl The following are substituted into kadmin/testing/scripts/env-setup.sh + +# The following are substituted into kadmin/testing/scripts/env-setup.sh RBUILD=`pwd` AC_SUBST(RBUILD) case "$srcdir" in @@ -688,18 +688,18 @@ esac AC_SUBST(S_TOP) AC_PATH_PROG(PERL_PATH,perl) AC_PATH_PROG(EXPECT,expect) -dnl For kadmin/testing/util/Makefile.in +# For kadmin/testing/util/Makefile.in if test "$TCL_LIBS" != "" ; then DO_ALL=tcl fi AC_SUBST(DO_ALL) KRB5_AC_PRIOCNTL_HACK K5_GEN_FILE(kadmin/testing/scripts/env-setup.sh:kadmin/testing/scripts/env-setup.shin) -dnl for lib/kadm5 +# for lib/kadm5 AC_CHECK_PROG(RUNTEST,runtest,runtest) AC_CHECK_PROG(PERL,perl,perl) -dnl -dnl lib/gssapi + +# lib/gssapi AC_CHECK_HEADER(stdint.h,[ include_stdint='awk '\''END{printf("%cinclude \n", 35);}'\'' < /dev/null'], include_stdint='echo "/* no stdint.h */"') @@ -712,24 +712,24 @@ AC_CHECK_HEADER(xom.h,[ include_xom='awk '\''END{printf("%cinclude \n", 35);}'\'' < /dev/null'], [ include_xom='echo "/* no xom.h */"']) AC_SUBST(include_xom) -dnl -dnl -dnl lib/rpc + + +# lib/rpc ### Check where struct rpcent is declared. -# + # This is necessary to determine: # 1. If /usr/include/netdb.h declares struct rpcent # 2. If /usr/include/rpc/netdb.h declares struct rpcent -# + # We have our own rpc/netdb.h, and if /usr/include/netdb.h includes # rpc/netdb.h, then nastiness could happen. -# + # Logic: If /usr/include/netdb.h declares struct rpcent, then check # rpc/netdb.h. If /usr/include/rpc/netdb.h declares struct rpcent, # then define STRUCT_RPCENT_IN_RPC_NETDB_H, otherwise do not. If # neither netdb.h nor rpc/netdb.h declares struct rpcent, then define # STRUCT_RPCENT_IN_RPC_NETDB_H anyway. -# + AC_MSG_CHECKING([where struct rpcent is declared]) AC_TRY_COMPILE([#include ], [struct rpcent e; @@ -847,9 +847,9 @@ else GSSRPC__BSD_TYPEALIASES='#define GSSRPC__BSD_TYPEALIASES 1' fi AC_SUBST(GSSRPC__BSD_TYPEALIASES) -# + # sockaddr length field checks -# + AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , , [#include @%:@include ]) @@ -864,10 +864,10 @@ AC_CACHE_VAL(k5_cv_type_setrpcent, extern "C" #endif extern void setrpcent();], -[int i;], k5_cv_type_setrpcent=void, k5_cv_type_setrpcent=int)])dnl +[int i;], k5_cv_type_setrpcent=void, k5_cv_type_setrpcent=int)]) AC_MSG_RESULT($k5_cv_type_setrpcent) AC_DEFINE_UNQUOTED(SETRPCENT_TYPE, $k5_cv_type_setrpcent, [Define as return type of setrpcent]) -dnl + AC_MSG_CHECKING([return type of endrpcent]) AC_CACHE_VAL(k5_cv_type_endrpcent, [AC_TRY_COMPILE([#include @@ -875,7 +875,7 @@ AC_CACHE_VAL(k5_cv_type_endrpcent, extern "C" #endif extern void endrpcent();], -[int i;], k5_cv_type_endrpcent=void, k5_cv_type_endrpcent=int)])dnl +[int i;], k5_cv_type_endrpcent=void, k5_cv_type_endrpcent=int)]) AC_MSG_RESULT($k5_cv_type_endrpcent) AC_DEFINE_UNQUOTED(ENDRPCENT_TYPE, $k5_cv_type_endrpcent, [Define as return type of endrpcent]) K5_GEN_FILE(include/gssrpc/types.h:include/gssrpc/types.hin) @@ -891,7 +891,7 @@ esac changequote([, ]) AC_SUBST(PASS) -dnl for pkinit +# for pkinit AC_ARG_ENABLE([pkinit], [ --disable-pkinit disable PKINIT plugin support],, enable_pkinit=try) @@ -916,10 +916,10 @@ else AC_MSG_NOTICE([Disabling PKINIT support.]) fi -dnl for lib/apputils +# for lib/apputils AC_REPLACE_FUNCS(daemon) -dnl for tests/ +# for tests/ if test x"$RUNTEST" != x; then HAVE_RUNTEST=yes else @@ -927,11 +927,11 @@ else fi AC_SUBST(HAVE_RUNTEST) -dnl for plugins/kdb/db2 -dnl -dnl AIX is unusual in that it wants all symbols resolved at link time -dnl Fortunately, it will allow us to link the kdb library now, even if -dnl it is linked again later. +# for plugins/kdb/db2 + +# AIX is unusual in that it wants all symbols resolved at link time +# Fortunately, it will allow us to link the kdb library now, even if +# it is linked again later. case $krb5_cv_host in *-*-aix*) DB_EXTRA_LIBS=-ldb @@ -942,14 +942,14 @@ case $krb5_cv_host in esac AC_SUBST(DB_EXTRA_LIBS) -dnl -dnl -dnl Check for thread safety issues. -dnl (Is there a better place for this?) -dnl tsfuncs="getpwnam_r getpwuid_r gethostbyname_r getservbyname_r gmtime_r localtime_r" -dnl Removed getpwnam_r and getpwuid_r because include/configure.in has some -dnl more careful checks, and may decide to pretend that they're not found if -dnl the function signatures can't be figured out. + + +# Check for thread safety issues. +# (Is there a better place for this?) +# tsfuncs="getpwnam_r getpwuid_r gethostbyname_r getservbyname_r gmtime_r localtime_r" +# Removed getpwnam_r and getpwuid_r because include/configure.in has some +# more careful checks, and may decide to pretend that they're not found if +# the function signatures can't be figured out. tsfuncs="gethostbyname_r getservbyname_r gmtime_r localtime_r" AC_CHECK_FUNCS($tsfuncs) if test "$enable_thread_support" = yes; then @@ -972,34 +972,34 @@ if test "$enable_thread_support" = yes; then AC_MSG_WARN([may not be thread-safe.]) fi # tsmissing not empty fi # enable_thread_support -dnl -dnl Sadly, we seem to have accidentally committed ourselves in 1.4 to -dnl an ABI that includes the existence of libkrb5support.0 even -dnl though random apps should never use anything from it. And on -dnl the Mac, to which that didn't apply, we can't use major version 0. -dnl + +# Sadly, we seem to have accidentally committed ourselves in 1.4 to +# an ABI that includes the existence of libkrb5support.0 even +# though random apps should never use anything from it. And on +# the Mac, to which that didn't apply, we can't use major version 0. + case $krb5_cv_host in *-*-darwin* | *-*-rhapsody*) SUPPORTLIB_MAJOR=1 ;; *) SUPPORTLIB_MAJOR=0 ;; esac AC_SUBST(SUPPORTLIB_MAJOR) -dnl -dnl On the Mac we need CoreFoundation for UCS-2 conversion for RC4. + +# On the Mac we need CoreFoundation for UCS-2 conversion for RC4. case $krb5_cv_host in *-*-darwin* | *-*-rhapsody*) CRYPTO_LIBS="-framework CoreFoundation" ;; *) CRYPTO_LIBS="" ;; esac AC_SUBST(CRYPTO_LIBS) -dnl -dnl + + if test "$COM_ERR_VERSION" = k5 ; then K5_GEN_MAKEFILE(util/et) fi if test "$SS_VERSION" = k5 ; then K5_GEN_MAKEFILE(util/ss) fi -dnl -dnl + + ldap_plugin_dir="" ldap_lib="" if test -n "$OPENLDAP_PLUGIN"; then @@ -1036,20 +1036,20 @@ fi AC_SUBST(ldap_plugin_dir) AC_SUBST(LDAP) -dnl We really should look for and use python-config. +# We really should look for and use python-config. PYTHON_LIB= AC_CHECK_HEADERS(Python.h python2.3/Python.h python2.5/Python.h) AC_CHECK_LIB(python2.3,main,[PYTHON_LIB=-lpython2.3], AC_CHECK_LIB(python2.5,main,[PYTHON_LIB=-lpython2.5])) AC_SUBST(PYTHON_LIB) -dnl -dnl Kludge for simple server --- FIXME is this the best way to do this? -dnl + +# Kludge for simple server --- FIXME is this the best way to do this? + if test "$ac_cv_lib_socket" = "yes" -a "$ac_cv_lib_nsl" = "yes"; then AC_DEFINE(BROKEN_STREAMS_SOCKETS,1,[Define if socket can't be bound to 0.0.0.0]) fi -dnl + AC_CONFIG_SUBDIRS(appl/libpty appl/bsd appl/gssftp appl/telnet) AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config]) @@ -1060,7 +1060,7 @@ V5_AC_OUTPUT_MAKEFILE(. lib lib/kdb lib/crypto lib/crypto/krb lib/crypto/krb/crc32 lib/crypto/builtin/des - lib/crypto/krb/dk lib/crypto/krb/enc_provider + lib/crypto/krb/dk lib/crypto/builtin/enc_provider lib/crypto/krb/hash_provider lib/crypto/krb/keyhash_provider lib/crypto/builtin lib/crypto/builtin/md4 lib/crypto/builtin/md5 lib/crypto/krb/old lib/crypto/krb/raw lib/crypto/builtin/sha1 diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 0578e6b63e..bac2b0cfe9 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -675,6 +675,7 @@ struct krb5_enc_provider { }; struct krb5_hash_provider { + char hash_name[8]; size_t hashsize, blocksize; /* this takes multiple inputs to avoid lots of copying. */ @@ -965,6 +966,21 @@ typedef struct _krb5_pa_for_user { krb5_data auth_package; } krb5_pa_for_user; +typedef struct _krb5_s4u_userid { + krb5_int32 nonce; + krb5_principal user; + krb5_data subject_cert; + krb5_flags options; +} krb5_s4u_userid; + +#define KRB5_S4U_OPTS_CHECK_LOGON_HOURS 0x40000000 /* check logon hour restrictions */ +#define KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE 0x20000000 /* sign with usage 27 instead of 26 */ + +typedef struct _krb5_pa_s4u_x509_user { + krb5_s4u_userid user_id; + krb5_checksum cksum; +} krb5_pa_s4u_x509_user; + enum { KRB5_FAST_ARMOR_AP_REQUEST = 0x1 }; @@ -1299,6 +1315,10 @@ void KRB5_CALLCONV krb5_free_pa_enc_ts (krb5_context, krb5_pa_enc_ts *); void KRB5_CALLCONV krb5_free_pa_for_user (krb5_context, krb5_pa_for_user * ); +void KRB5_CALLCONV krb5_free_s4u_userid_contents + (krb5_context, krb5_s4u_userid * ); +void KRB5_CALLCONV krb5_free_pa_s4u_x509_user + (krb5_context, krb5_pa_s4u_x509_user * ); void KRB5_CALLCONV krb5_free_pa_svr_referral_data (krb5_context, krb5_pa_svr_referral_data * ); void KRB5_CALLCONV krb5_free_pa_server_referral_data @@ -1708,6 +1728,12 @@ krb5_error_code encode_krb5_setpw_req krb5_error_code encode_krb5_pa_for_user (const krb5_pa_for_user * , krb5_data **); +krb5_error_code encode_krb5_s4u_userid + (const krb5_s4u_userid * , krb5_data **); + +krb5_error_code encode_krb5_pa_s4u_x509_user + (const krb5_pa_s4u_x509_user * , krb5_data **); + krb5_error_code encode_krb5_pa_svr_referral_data (const krb5_pa_svr_referral_data * , krb5_data **); @@ -1880,6 +1906,9 @@ krb5_error_code decode_krb5_setpw_req krb5_error_code decode_krb5_pa_for_user (const krb5_data *, krb5_pa_for_user **); +krb5_error_code decode_krb5_pa_s4u_x509_user + (const krb5_data *, krb5_pa_s4u_x509_user **); + krb5_error_code decode_krb5_pa_svr_referral_data (const krb5_data *, krb5_pa_svr_referral_data **); @@ -2711,6 +2740,11 @@ krb5_error_code krb5int_send_tgs krb5_pa_data * const *, const krb5_data *, krb5_creds *, + krb5_error_code (*gcvt_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *gcvt_data, krb5_response * , krb5_keyblock **subkey); /* The subkey field is an output parameter; if a * tgs-rep is received then the subkey will be filled @@ -2914,6 +2948,21 @@ krb5int_pac_sign(krb5_context context, const krb5_keyblock *privsvr_key, krb5_data *data); +krb5_error_code KRB5_CALLCONV +krb5_get_credentials_for_user(krb5_context context, krb5_flags options, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_data *cert, + krb5_creds **out_creds); + +krb5_error_code KRB5_CALLCONV +krb5_get_credentials_for_proxy(krb5_context context, + krb5_flags options, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_ticket *evidence_tkt, + krb5_creds **out_creds); + krb5_error_code krb5int_parse_enctype_list(krb5_context context, char *profstr, krb5_enctype *default_list, krb5_enctype **result); diff --git a/src/include/kdb.h b/src/include/kdb.h index ea81cfeef1..8c0cd247a2 100644 --- a/src/include/kdb.h +++ b/src/include/kdb.h @@ -96,6 +96,8 @@ #define KRB5_KDB_SUPPORT_DESMD5 0x00004000 #define KRB5_KDB_NEW_PRINC 0x00008000 #define KRB5_KDB_OK_AS_DELEGATE 0x00100000 +#define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE 0x00200000 /* S4U2Self OK */ +#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000 /* Creation flags */ #define KRB5_KDB_CREATE_BTREE 0x00000001 diff --git a/src/include/kdb_ext.h b/src/include/kdb_ext.h index 5695971697..3841920058 100644 --- a/src/include/kdb_ext.h +++ b/src/include/kdb_ext.h @@ -31,10 +31,6 @@ #ifndef KRB5_KDB5_EXT__ #define KRB5_KDB5_EXT__ -/* Allowed to use protocol transition */ -#define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE 0x00200000 -/* Service does not require authorization data */ -#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000 /* Private flag used to indicate principal is local TGS */ #define KRB5_KDB_TICKET_GRANTING_SERVICE 0x01000000 /* Private flag used to indicate xrealm relationship is non-transitive */ diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index ea7475261e..81bc1cf6e5 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -631,6 +631,11 @@ krb5_error_code KRB5_CALLCONV /* Defined in KDC referrals draft */ #define KRB5_KEYUSAGE_PA_REFERRAL 26 /* XXX note conflict with above */ + +/* Defined in [MS-SFU] */ +#define KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST 26 /* XXX note conflict with above */ +#define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY 27 /* XXX note conflict with above */ + /* define in draft-ietf-krb-wg-preauth-framework*/ #define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50 #define KRB5_KEYUSAGE_FAST_ENC 51 @@ -1566,6 +1571,10 @@ void KRB5_CALLCONV krb5_free_tgt_creds #define KRB5_GC_USER_USER 1 /* want user-user ticket */ #define KRB5_GC_CACHED 2 /* want cached ticket only */ #define KRB5_GC_CANONICALIZE 4 /* set canonicalize KDC option */ +#define KRB5_GC_NO_STORE 8 /* do not store in credentials cache */ +#define KRB5_GC_FORWARDABLE 16 /* acquire forwardable tickets */ +#define KRB5_GC_NO_TRANSIT_CHECK 32 /* disable transited check */ +#define KRB5_GC_CONSTRAINED_DELEGATION 64 /* constrained delegation */ krb5_error_code KRB5_CALLCONV krb5_get_credentials (krb5_context, diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index 814ace35cc..513e716bbe 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -72,7 +72,9 @@ static struct pflag flags[] = { {"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, {"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 }, {"support_desmd5", 14, KRB5_KDB_SUPPORT_DESMD5, 0 }, -{"ok_as_delegate", 14, KRB5_KDB_OK_AS_DELEGATE, 0 } +{"ok_as_delegate", 14, KRB5_KDB_OK_AS_DELEGATE, 0 }, +{"ok_to_auth_as_delegate", 22, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, 0 }, +{"no_auth_data_required", 21, KRB5_KDB_NO_AUTH_DATA_REQUIRED, 0}, }; static char *prflags[] = { @@ -97,6 +99,8 @@ static char *prflags[] = { "UNKNOWN_0x00040000", /* 0x00040000 */ "UNKNOWN_0x00080000", /* 0x00080000 */ "OK_AS_DELEGATE", /* 0x00100000 */ + "OK_TO_AUTH_AS_DELEGATE", /* 0x00200000 */ + "NO_AUTH_DATA_REQUIRED", /* 0x00400000 */ }; char *getenv(); @@ -1123,7 +1127,7 @@ kadmin_addprinc_usage(func) "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n", "\t\trequires_hwauth needchange allow_svr password_changing_service\n" - "\t\tok_as_delegate\n" + "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n" "\nwhere,\n\t[-x db_princ_args]* - any number of database specific arguments.\n" "\t\t\tLook at each database documentation for supported arguments\n"); } @@ -1140,7 +1144,7 @@ kadmin_modprinc_usage(func) "\t\tallow_postdated allow_forwardable allow_tgs_req allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n", "\t\trequires_hwauth needchange allow_svr password_changing_service\n" - "\t\tok_as_delegate\n" + "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n" "\nwhere,\n\t[-x db_princ_args]* - any number of database specific arguments.\n" "\t\t\tLook at each database documentation for supported arguments\n" ); diff --git a/src/kadmin/testing/scripts/env-setup.shin b/src/kadmin/testing/scripts/env-setup.shin index 519b9864e1..be07151104 100755 --- a/src/kadmin/testing/scripts/env-setup.shin +++ b/src/kadmin/testing/scripts/env-setup.shin @@ -90,21 +90,15 @@ if [ "$TEST_PATH" != "" ]; then fi if [ "x$PS_ALL" = "x" ]; then - ps -axwwu >/dev/null 2>&1 - ps_bsd=$? - - ps -ef >/dev/null 2>&1 - ps_sysv=$? - - if [ $ps_bsd = 0 -a $ps_sysv = 1 ]; then - PS_ALL="ps -auxww" - PS_PID="ps -auxww" - elif [ $ps_bsd = 1 -a $ps_sysv = 0 ]; then + if ps auxww >/dev/null 2>&1; then + PS_ALL="ps auxww" + PS_PID="ps auxww" + elif ps -ef >/dev/null 2>&1; then PS_ALL="ps -ef" PS_PID="ps -fp" else - PS_ALL="ps -auxww" - PS_PID="ps -auxww" + PS_ALL="ps auxww" + PS_PID="ps auxww" echo "WARNING! Cannot auto-detect ps type, assuming BSD." fi diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c index 15ae99fdde..6679ce0a7c 100644 --- a/src/kadmin/testing/util/tcl_kadm5.c +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -2033,7 +2033,7 @@ static int tcl_kadm5_randkey_principal(ClientData clientData, ret = kadm5_randkey_principal(server_handle, princ, keyblock_var ? &keyblocks : 0, - num_var ? &num_keys : 0); + &num_keys); if (ret == KADM5_OK) { if (keyblock_var) { diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index 8b1c47387b..48d8f3886e 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -117,7 +117,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */ krb5_db_entry client, krbtgt; int c_nprincs = 0, k_nprincs = 0; - krb5_pa_for_user *for_user = NULL; /* protocol transition request */ + krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */ krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */ unsigned int c_flags = 0, s_flags = 0; /* client/server KDB flags */ char *s4u_name = NULL; @@ -131,7 +131,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_data scratch; session_key.contents = NULL; - + retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; @@ -292,12 +292,20 @@ tgt_again: !krb5_principal_compare(kdc_context, tgs_server, server.princ); /* Check for protocol transition */ - errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client, - &server, header_enc_tkt->session, kdc_time, - &for_user, &client, &c_nprincs, &status); + errcode = kdc_process_s4u2self_req(kdc_context, + request, + header_enc_tkt->client, + &server, + subkey, + header_enc_tkt->session, + kdc_time, + &s4u_x509_user, + &client, + &c_nprincs, + &status); if (errcode) goto cleanup; - if (for_user != NULL) + if (s4u_x509_user != NULL) setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION); /* @@ -438,19 +446,32 @@ tgt_again: /* processing of any of these flags. For example, some */ /* realms may refuse to issue renewable tickets */ - if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) + if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); - if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { - if (!krb5_is_tgs_principal(server.princ) && - is_local_principal(server.princ)) { - if (isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)) - setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); - else + + if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { + /* + * If S4U2Self principal is not forwardable, then mark ticket as + * unforwardable. This behaviour matches Windows, but it is + * different to the MIT AS-REQ path, which returns an error + * (KDC_ERR_POLICY) if forwardable tickets cannot be issued. + * + * Consider this block the S4U2Self equivalent to + * validate_forwardable(). + */ + if (c_nprincs && + isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) + clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); + /* + * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting + * S4U2Self in order for forwardable tickets to be returned. + */ + else if (!is_referral && + !isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)) clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); } - if (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) - clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); } + if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); @@ -560,7 +581,7 @@ tgt_again: enc_tkt_reply.times.starttime = 0; if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) { - errcode = krb5_unparse_name(kdc_context, for_user->user, &s4u_name); + errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name); } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) { errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name); } else { @@ -670,8 +691,8 @@ tgt_again: enc_tkt_reply.authorization_data = NULL; if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && - is_local_principal(header_enc_tkt->client)) - enc_tkt_reply.client = for_user->user; + !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) + enc_tkt_reply.client = s4u_x509_user->user_id.user; else enc_tkt_reply.client = header_enc_tkt->client; @@ -689,7 +710,8 @@ tgt_again: &encrypting_key, /* U2U or server key */ pkt, request, - for_user ? for_user->user : NULL, + s4u_x509_user ? + s4u_x509_user->user_id.user : NULL, header_enc_tkt, &enc_tkt_reply); if (errcode) { @@ -845,6 +867,20 @@ tgt_again: /* Start assembling the response */ reply.msg_type = KRB5_TGS_REP; reply.padata = 0;/* always */ + if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && + find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) { + errcode = kdc_make_s4u2self_rep(kdc_context, + subkey, + header_ticket->enc_part2->session, + s4u_x509_user, + &reply, + &reply_encpart); + if (errcode) { + status = "KDC_RETURN_S4U2SELF_PADATA"; + goto cleanup; + } + } + reply.client = enc_tkt_reply.client; reply.enc_part.kvno = 0;/* We are using the session key */ reply.ticket = &ticket_reply; @@ -958,14 +994,18 @@ cleanup: krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs); if (c_nprincs) krb5_db_free_principal(kdc_context, &client, c_nprincs); - if (for_user != NULL) - krb5_free_pa_for_user(kdc_context, for_user); + if (s4u_x509_user != NULL) + krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user); if (kdc_issued_auth_data != NULL) krb5_free_authdata(kdc_context, kdc_issued_auth_data); if (s4u_name != NULL) free(s4u_name); if (subkey != NULL) krb5_free_keyblock(kdc_context, subkey); + if (reply.padata) + krb5_free_pa_data(kdc_context, reply.padata); + if (reply_encpart.enc_padata) + krb5_free_pa_data(kdc_context, reply_encpart.enc_padata); return retval; } diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c index 493e2397e7..82f934f57f 100644 --- a/src/kdc/kdc_authdata.c +++ b/src/kdc/kdc_authdata.c @@ -549,9 +549,18 @@ handle_tgt_authdata (krb5_context context, } if (ad_nprincs != 0) { + /* + * This code was submitted by Novell; however there is no + * mention in [MS-SFU] of needing to examine the authorization + * data to clear the forwardable flag. My understanding is that + * the state of the forwardable flag is propagated through the + * cross-realm TGTs. + */ +#if 0 if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE); +#endif krb5_db_free_principal(context, &ad_entry, ad_nprincs); diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c index cc7ae34ed4..2149fd1ac2 100644 --- a/src/kdc/kdc_preauth.c +++ b/src/kdc/kdc_preauth.c @@ -1348,25 +1348,6 @@ cleanup: return (retval); } -static krb5_boolean -enctype_requires_etype_info_2(krb5_enctype enctype) -{ - switch(enctype) { - case ENCTYPE_DES_CBC_CRC: - case ENCTYPE_DES_CBC_MD4: - case ENCTYPE_DES_CBC_MD5: - case ENCTYPE_DES3_CBC_SHA1: - case ENCTYPE_DES3_CBC_RAW: - case ENCTYPE_ARCFOUR_HMAC: - case ENCTYPE_ARCFOUR_HMAC_EXP : - return 0; - default: - if (krb5_c_valid_enctype(enctype)) - return 1; - else return 0; - } -} - static krb5_boolean request_contains_enctype (krb5_context context, const krb5_kdc_req *request, krb5_enctype enctype) diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index 8dd4f91dec..9aada81329 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -223,7 +223,7 @@ comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket, krb5_pa_data * find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type) { -return krb5int_find_pa_data(kdc_context, padata, pa_type); + return krb5int_find_pa_data(kdc_context, padata, pa_type); } krb5_error_code @@ -371,7 +371,8 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from, } /* make sure the client is of proper lineage (see above) */ - if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) { + if (foreign_server && + !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) { if (is_local_principal((*ticket)->enc_part2->client)) { /* someone in a foreign realm claiming to be local */ krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check"); @@ -926,7 +927,8 @@ fail: * as a com_err error number! */ #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\ -KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY) + KDC_OPT_VALIDATE | KDC_OPT_RENEW | \ + KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT) int validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, krb5_db_entry server, krb5_timestamp kdc_time, @@ -998,17 +1000,9 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, * preauthentication data is absent in the request. * * Hence, this check most be done after the check for preauth - * data, and is now performed by validate_forwardable(). + * data, and is now performed by validate_forwardable() (the + * contents of which were previously below). */ -#if 0 - /* Client and server must allow forwardable tickets */ - if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && - (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) || - isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) { - *status = "FORWARDABLE NOT ALLOWED"; - return(KDC_ERR_POLICY); - } -#endif /* Client and server must allow renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && @@ -1795,7 +1789,7 @@ sign_db_authdata (krb5_context context, } static krb5_error_code -verify_s4u2self_checksum(krb5_context context, +verify_for_user_checksum(krb5_context context, krb5_keyblock *key, krb5_pa_for_user *req) { @@ -1854,7 +1848,7 @@ verify_s4u2self_checksum(krb5_context context, &valid); if (code == 0 && valid == FALSE) - code = KRB5KRB_AP_ERR_BAD_INTEGRITY; + code = KRB5KRB_AP_ERR_MODIFIED; free(data.data); @@ -1862,55 +1856,246 @@ verify_s4u2self_checksum(krb5_context context, } /* - * Protocol transition validation code based on AS-REQ - * validation code + * Legacy protocol transition (Windows 2003 and above) */ -static int -validate_s4u2self_request(krb5_kdc_req *request, - const krb5_db_entry *client, - krb5_timestamp kdc_time, - const char **status) +static krb5_error_code +kdc_process_for_user(krb5_context context, + krb5_pa_data *pa_data, + krb5_keyblock *tgs_session, + krb5_pa_s4u_x509_user **s4u_x509_user, + const char **status) { - int errcode; - krb5_db_entry server = { 0 }; - - /* The client must not be expired */ - if (client->expiration && client->expiration < kdc_time) { - *status = "CLIENT EXPIRED"; - return KDC_ERR_NAME_EXP; + krb5_error_code code; + krb5_pa_for_user *for_user; + krb5_data req_data; + + req_data.length = pa_data->length; + req_data.data = (char *)pa_data->contents; + + code = decode_krb5_pa_for_user(&req_data, &for_user); + if (code) + return code; + + code = verify_for_user_checksum(context, tgs_session, for_user); + if (code) { + *status = "INVALID_S4U2SELF_CHECKSUM"; + krb5_free_pa_for_user(kdc_context, for_user); + return code; } - /* The client's password must not be expired, unless the server is - a KRB5_KDC_PWCHANGE_SERVICE. */ - if (client->pw_expiration && client->pw_expiration < kdc_time) { - *status = "CLIENT KEY EXPIRED"; - return KDC_ERR_KEY_EXP; + *s4u_x509_user = calloc(1, sizeof(krb5_pa_s4u_x509_user)); + if (*s4u_x509_user == NULL) { + krb5_free_pa_for_user(kdc_context, for_user); + return ENOMEM; } + (*s4u_x509_user)->user_id.user = for_user->user; + for_user->user = NULL; + krb5_free_pa_for_user(context, for_user); + + return 0; +} + +static krb5_error_code +verify_s4u_x509_user_checksum(krb5_context context, + krb5_keyblock *key, + krb5_data *req_data, + krb5_int32 kdc_req_nonce, + krb5_pa_s4u_x509_user *req) +{ + krb5_error_code code; + krb5_data scratch; + krb5_boolean valid = FALSE; + + if (enctype_requires_etype_info_2(key->enctype) && + !krb5_c_is_keyed_cksum(req->cksum.checksum_type)) + return KRB5KRB_AP_ERR_INAPP_CKSUM; + + if (req->user_id.nonce != kdc_req_nonce) + return KRB5KRB_AP_ERR_MODIFIED; + /* - * If the client requires password changing, then return an - * error; S4U2Self cannot be used to change a password. + * Verify checksum over the encoded userid. If that fails, + * re-encode, and verify that. This is similar to the + * behaviour in kdc_process_tgs_req(). */ - if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) { - *status = "REQUIRED PWCHANGE"; - return KDC_ERR_KEY_EXP; + if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0) + return ASN1_PARSE_ERROR; + + code = krb5_c_verify_checksum(context, + key, + KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST, + &scratch, + &req->cksum, + &valid); + if (code != 0) + return code; + + if (valid == FALSE) { + krb5_data *data; + + code = encode_krb5_s4u_userid(&req->user_id, &data); + if (code != 0) + return code; + + code = krb5_c_verify_checksum(context, + key, + KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST, + data, + &req->cksum, + &valid); + + krb5_free_data(context, data); + + if (code != 0) + return code; } - /* Check to see if client is locked out */ - if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { - *status = "CLIENT LOCKED OUT"; - return KDC_ERR_C_PRINCIPAL_UNKNOWN; + return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED; +} + +/* + * New protocol transition request (Windows 2008 and above) + */ +static krb5_error_code +kdc_process_s4u_x509_user(krb5_context context, + krb5_kdc_req *request, + krb5_pa_data *pa_data, + krb5_keyblock *tgs_subkey, + krb5_keyblock *tgs_session, + krb5_pa_s4u_x509_user **s4u_x509_user, + const char **status) +{ + krb5_error_code code; + krb5_data req_data; + + req_data.length = pa_data->length; + req_data.data = (char *)pa_data->contents; + + code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user); + if (code) + return code; + + code = verify_s4u_x509_user_checksum(context, + tgs_subkey ? tgs_subkey : + tgs_session, + &req_data, + request->nonce, *s4u_x509_user); + + if (code) { + *status = "INVALID_S4U2SELF_CHECKSUM"; + krb5_free_pa_s4u_x509_user(context, *s4u_x509_user); + *s4u_x509_user = NULL; + return code; } + if (krb5_princ_size(context, (*s4u_x509_user)->user_id.user) == 0 || + (*s4u_x509_user)->user_id.subject_cert.length != 0) { + *status = "INVALID_S4U2SELF_REQUEST"; + krb5_free_pa_s4u_x509_user(context, *s4u_x509_user); + *s4u_x509_user = NULL; + return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + } + + return 0; +} + +krb5_error_code +kdc_make_s4u2self_rep(krb5_context context, + krb5_keyblock *tgs_subkey, + krb5_keyblock *tgs_session, + krb5_pa_s4u_x509_user *req_s4u_user, + krb5_kdc_rep *reply, + krb5_enc_kdc_rep_part *reply_encpart) +{ + krb5_error_code code; + krb5_data *data = NULL; + krb5_pa_s4u_x509_user rep_s4u_user; + krb5_pa_data padata; + krb5_enctype enctype; + krb5_keyusage usage; + + memset(&rep_s4u_user, 0, sizeof(rep_s4u_user)); + + rep_s4u_user.user_id.nonce = req_s4u_user->user_id.nonce; + rep_s4u_user.user_id.user = req_s4u_user->user_id.user; + rep_s4u_user.user_id.options = + req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE; + + code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data); + if (code != 0) + goto cleanup; + + if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) + usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY; + else + usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST; + + code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type, + tgs_subkey != NULL ? tgs_subkey : tgs_session, + usage, data, + &rep_s4u_user.cksum); + if (code != 0) + goto cleanup; + + krb5_free_data(context, data); + data = NULL; + + code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data); + if (code != 0) + goto cleanup; + + padata.magic = KV5M_PA_DATA; + padata.pa_type = KRB5_PADATA_S4U_X509_USER; + padata.length = data->length; + padata.contents = (krb5_octet *)data->data; + + code = add_pa_data_element(context, &padata, &reply->padata, FALSE); + if (code != 0) + goto cleanup; + + free(data); + data = NULL; + + if (tgs_subkey != NULL) + enctype = tgs_subkey->enctype; + else + enctype = tgs_session->enctype; + /* - * Check against local policy + * Owing to a bug in Windows, unkeyed checksums were used for older + * enctypes, including rc4-hmac. A forthcoming workaround for this + * includes the checksum bytes in the encrypted padata. */ - errcode = against_local_policy_as(request, *client, server, - kdc_time, status); - if (errcode) - return errcode; + if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) && + enctype_requires_etype_info_2(enctype) == FALSE) { + padata.length = req_s4u_user->cksum.length + + rep_s4u_user.cksum.length; + padata.contents = malloc(padata.length); + if (padata.contents == NULL) { + code = ENOMEM; + goto cleanup; + } - return 0; + memcpy(padata.contents, + req_s4u_user->cksum.contents, + req_s4u_user->cksum.length); + memcpy(&padata.contents[req_s4u_user->cksum.length], + rep_s4u_user.cksum.contents, + rep_s4u_user.cksum.length); + + code = add_pa_data_element(context,&padata, + &reply_encpart->enc_padata, FALSE); + if (code != 0) + goto cleanup; + } + +cleanup: + if (rep_s4u_user.cksum.contents != NULL) + krb5_free_checksum_contents(context, &rep_s4u_user.cksum); + krb5_free_data(context, data); + + return code; } /* @@ -1921,92 +2106,116 @@ kdc_process_s4u2self_req(krb5_context context, krb5_kdc_req *request, krb5_const_principal client_princ, const krb5_db_entry *server, - krb5_keyblock *subkey, + krb5_keyblock *tgs_subkey, + krb5_keyblock *tgs_session, krb5_timestamp kdc_time, - krb5_pa_for_user **for_user, + krb5_pa_s4u_x509_user **s4u_x509_user, krb5_db_entry *princ, int *nprincs, const char **status) { krb5_error_code code; - krb5_pa_data **pa_data; - krb5_data req_data; + krb5_pa_data *pa_data; krb5_boolean more; + int flags; *nprincs = 0; memset(princ, 0, sizeof(*princ)); - if (request->padata == NULL) { - return 0; - } - - for (pa_data = request->padata; *pa_data != NULL; pa_data++) { - if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER) - break; - } - if (*pa_data == NULL) { - return 0; + pa_data = find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER); + if (pa_data != NULL) { + code = kdc_process_s4u_x509_user(context, + request, + pa_data, + tgs_subkey, + tgs_session, + s4u_x509_user, + status); + if (code != 0) + return code; + } else { + pa_data = find_pa_data(request->padata, KRB5_PADATA_FOR_USER); + if (pa_data != NULL) { + code = kdc_process_for_user(context, + pa_data, + tgs_session, + s4u_x509_user, + status); + if (code != 0) + return code; + } else + return 0; } -#if 0 /* - * Ignore request if the server principal is a TGS, not so much - * to avoid unconstrained tickets being issued (as that would - * require knowing the TGS key anyway) but so that we do not - * block the server referral path. + * We need to compare the client name in the TGT with the requested + * server name. Supporting server name aliases without assuming a + * global name service makes this difficult to do. + * + * The comparison below handles the following cases (note that the + * term "principal name" below excludes the realm). + * + * (1) The requested service is a host-based service with two name + * components, in which case we assume the principal name to + * contain sufficient qualifying information. The realm is + * ignored for the purpose of comparison. + * + * (2) The requested service name is an enterprise principal name: + * the service principal name is compared with the unparsed + * form of the client name (including its realm). + * + * (3) The requested service is some other name type: an exact + * match is required. + * + * An alternative would be to look up the server once again with + * FLAG_CANONICALIZE | FLAG_CLIENT_REFERRALS_ONLY set, do an exact + * match between the returned name and client_princ. However, this + * assumes that the client set FLAG_CANONICALIZE when requesting + * the TGT and that we have a global name service. */ - if (krb5_is_tgs_principal(server->princ)) { - return 0; - } -#endif - - *status = "PROCESS_S4U2SELF_REQUEST"; - - req_data.length = (*pa_data)->length; - req_data.data = (char *)(*pa_data)->contents; - - code = decode_krb5_pa_for_user(&req_data, for_user); - if (code) { - return code; - } - - if (krb5_princ_type(context, (*for_user)->user) != - KRB5_NT_ENTERPRISE_PRINCIPAL) { - *status = "INVALID_S4U2SELF_REQUEST"; - return KRB5KDC_ERR_POLICY; + flags = 0; + switch (krb5_princ_type(kdc_context, request->server)) { + case KRB5_NT_SRV_HST: /* (1) */ + if (krb5_princ_size(kdc_context, request->server) == 2) + flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM; + break; + case KRB5_NT_ENTERPRISE_PRINCIPAL: /* (2) */ + flags |= KRB5_PRINCIPAL_COMPARE_ENTERPRISE; + break; + default: /* (3) */ + break; } - code = verify_s4u2self_checksum(context, subkey, *for_user); - if (code) { - *status = "INVALID_S4U2SELF_CHECKSUM"; - krb5_free_pa_for_user(kdc_context, *for_user); - *for_user = NULL; - return code; - } - if (!krb5_principal_compare_flags(context, request->server, client_princ, - KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) { + if (!krb5_principal_compare_flags(context, + request->server, + client_princ, + flags)) { *status = "INVALID_S4U2SELF_REQUEST"; - return KRB5KDC_ERR_POLICY; + return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error code */ } /* * Protocol transition is mutually exclusive with renew/forward/etc - * as well as user-to-user and constrained delegation. + * as well as user-to-user and constrained delegation. This check + * is also made in validate_as_request(). * * We can assert from this check that the header ticket was a TGT, as * that is validated previously in validate_tgs_request(). */ - if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) { + if (request->kdc_options & AS_INVALID_OPTIONS) { + *status = "INVALID AS OPTIONS"; return KRB5KDC_ERR_BADOPTION; } /* * Do not attempt to lookup principals in foreign realms. */ - if (is_local_principal((*for_user)->user)) { + if (is_local_principal((*s4u_x509_user)->user_id.user)) { + krb5_db_entry no_server; + *nprincs = 1; code = krb5_db_get_principal_ext(kdc_context, - (*for_user)->user, + (*s4u_x509_user)->user_id.user, KRB5_KDB_FLAG_INCLUDE_PAC, princ, nprincs, &more); if (code) { @@ -2023,14 +2232,15 @@ kdc_process_s4u2self_req(krb5_context context, return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; } - code = validate_s4u2self_request(request, princ, kdc_time, status); + memset(&no_server, 0, sizeof(no_server)); + + code = validate_as_request(request, *princ, + no_server, kdc_time, status); if (code) { return code; } } - *status = NULL; - return 0; } @@ -2051,7 +2261,7 @@ check_allowed_to_delegate_to(krb5_context context, /* Must be in same realm */ if (!krb5_realm_compare(context, server->princ, proxy)) { - return KRB5KDC_ERR_BADOPTION; + return KRB5KDC_ERR_POLICY; } req.server = server; @@ -2347,3 +2557,63 @@ log_tgs_alt_tgt(krb5_principal p) /* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */ } +krb5_boolean +enctype_requires_etype_info_2(krb5_enctype enctype) +{ + switch(enctype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES3_CBC_SHA1: + case ENCTYPE_DES3_CBC_RAW: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP : + return 0; + default: + return krb5_c_valid_enctype(enctype); + } +} + +/* XXX where are the generic helper routines for this? */ +krb5_error_code +add_pa_data_element(krb5_context context, + krb5_pa_data *padata, + krb5_pa_data ***inout_padata, + krb5_boolean copy) +{ + int i; + krb5_pa_data **p; + + if (*inout_padata != NULL) { + for (i = 0; (*inout_padata)[i] != NULL; i++) + ; + } else + i = 0; + + p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *)); + if (p == NULL) + return ENOMEM; + + *inout_padata = p; + + p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data)); + if (p[i] == NULL) + return ENOMEM; + *(p[i]) = *padata; + + p[i + 1] = NULL; + + if (copy) { + p[i]->contents = (krb5_octet *)malloc(padata->length); + if (p[i]->contents == NULL) { + free(p[i]); + p[i] = NULL; + return ENOMEM; + } + + memcpy(p[i]->contents, padata->contents, padata->length); + } + + return 0; +} + diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index e34ca876b4..26650510d8 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -150,6 +150,8 @@ int against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry, krb5_ticket *, const char **); /* kdc_preauth.c */ +krb5_boolean enctype_requires_etype_info_2(krb5_enctype enctype); + const char * missing_required_preauth (krb5_db_entry *client, krb5_db_entry *server, krb5_enc_tkt_part *enc_tkt_reply); @@ -177,6 +179,12 @@ krb5_error_code free_padata_context krb5_pa_data *find_pa_data (krb5_pa_data **padata, krb5_preauthtype pa_type); +krb5_error_code add_pa_data_element + (krb5_context context, + krb5_pa_data *padata, + krb5_pa_data ***out_padata, + krb5_boolean copy); + /* kdc_authdata.c */ krb5_error_code load_authdata_plugins(krb5_context context); krb5_error_code unload_authdata_plugins(krb5_context context); @@ -240,13 +248,22 @@ krb5_error_code kdc_process_s4u2self_req krb5_kdc_req *request, krb5_const_principal client_princ, const krb5_db_entry *server, - krb5_keyblock *subkey, + krb5_keyblock *tgs_subkey, + krb5_keyblock *tgs_session, krb5_timestamp kdc_time, - krb5_pa_for_user **s4u2_req, + krb5_pa_s4u_x509_user **s4u2self_req, krb5_db_entry *princ, int *nprincs, const char **status); +krb5_error_code kdc_make_s4u2self_rep + (krb5_context context, + krb5_keyblock *tgs_subkey, + krb5_keyblock *tgs_session, + krb5_pa_s4u_x509_user *req_s4u_user, + krb5_kdc_rep *reply, + krb5_enc_kdc_rep_part *reply_encpart); + krb5_error_code kdc_process_s4u2proxy_req (krb5_context context, krb5_kdc_req *request, diff --git a/src/lib/crypto/Makefile.in b/src/lib/crypto/Makefile.in index b68ef554fd..a6203b2fb1 100644 --- a/src/lib/crypto/Makefile.in +++ b/src/lib/crypto/Makefile.in @@ -2,7 +2,7 @@ thisconfigdir=../.. myfulldir=lib/crypto mydir=lib/crypto BUILDTOP=$(REL)..$(S).. -SUBDIRS=krb builtin crypto_tests +SUBDIRS= builtin krb crypto_tests RUN_SETUP = @KRB5_RUN_ENV@ PROG_LIBPATH=-L$(TOPLIBD) @@ -20,19 +20,19 @@ LIBINITFUNC=cryptoint_initialize_library LIBFINIFUNC=cryptoint_cleanup_library RELDIR=crypto -STOBJLISTS=krb/crc32/OBJS.ST krb/dk/OBJS.ST krb/enc_provider/OBJS.ST \ - krb/hash_provider/OBJS.ST krb/keyhash_provider/OBJS.ST \ - krb/old/OBJS.ST krb/raw/OBJS.ST krb/yarrow/OBJS.ST \ - @CRYPTO_IMPL@/md4/OBJS.ST @CRYPTO_IMPL@/md5/OBJS.ST @CRYPTO_IMPL@/sha1/OBJS.ST \ - @CRYPTO_IMPL@/arcfour/OBJS.ST @CRYPTO_IMPL@/aes/OBJS.ST @CRYPTO_IMPL@/des/OBJS.ST \ - krb/OBJS.ST @CRYPTO_IMPL@/OBJS.ST +STOBJLISTS=krb/crc32/OBJS.ST krb/dk/OBJS.ST builtin/enc_provider/OBJS.ST \ + krb/hash_provider/OBJS.ST krb/keyhash_provider/OBJS.ST \ + krb/old/OBJS.ST krb/raw/OBJS.ST krb/yarrow/OBJS.ST \ + builtin/md4/OBJS.ST builtin/md5/OBJS.ST builtin/sha1/OBJS.ST \ + builtin/arcfour/OBJS.ST builtin/aes/OBJS.ST builtin/des/OBJS.ST \ + krb/OBJS.ST builtin/OBJS.ST -SUBDIROBJLISTS=krb/crc32/OBJS.ST krb/dk/OBJS.ST krb/enc_provider/OBJS.ST \ - krb/hash_provider/OBJS.ST krb/keyhash_provider/OBJS.ST \ +SUBDIROBJLISTS=krb/crc32/OBJS.ST krb/dk/OBJS.ST builtin/enc_provider/OBJS.ST \ + krb/hash_provider/OBJS.ST krb/keyhash_provider/OBJS.ST \ krb/old/OBJS.ST krb/raw/OBJS.ST krb/yarrow/OBJS.ST \ - @CRYPTO_IMPL@/md4/OBJS.ST @CRYPTO_IMPL@/md5/OBJS.ST @CRYPTO_IMPL@/sha1/OBJS.ST \ - @CRYPTO_IMPL@/arcfour/OBJS.ST @CRYPTO_IMPL@/aes/OBJS.ST @CRYPTO_IMPL@/des/OBJS.ST \ - krb/OBJS.ST @CRYPTO_IMPL@/OBJS.ST + builtin/md4/OBJS.ST builtin/md5/OBJS.ST builtin/sha1/OBJS.ST \ + builtin/arcfour/OBJS.ST builtin/aes/OBJS.ST builtin/des/OBJS.ST \ + krb/OBJS.ST builtin/OBJS.ST # No dependencies. Record places to find this shared object if the target # link editor and loader support it. diff --git a/src/lib/crypto/builtin/Makefile.in b/src/lib/crypto/builtin/Makefile.in index 03ca5e9665..c1d8a554be 100644 --- a/src/lib/crypto/builtin/Makefile.in +++ b/src/lib/crypto/builtin/Makefile.in @@ -2,15 +2,16 @@ thisconfigdir=../../.. myfulldir=lib/crypto/builtin mydir=lib/crypto/builtin BUILDTOP=$(REL)..$(S)..$(S).. -SUBDIRS=../@CRYPTO_IMPL@/des ../@CRYPTO_IMPL@/arcfour ../@CRYPTO_IMPL@/aes \ - ../@CRYPTO_IMPL@/md4 ../@CRYPTO_IMPL@/md5 ../@CRYPTO_IMPL@/sha1 -LOCALINCLUDES = -I$(srcdir)/../krb -I$(srcdir)/../krb/hash_provider \ +SUBDIRS=des arcfour aes md4 md5 sha1 enc_provider +LOCALINCLUDES = -I$(srcdir)/../krb \ + -I$(srcdir)/../krb/hash_provider \ -I$(srcdir)/../@CRYPTO_IMPL@/des \ -I$(srcdir)/../@CRYPTO_IMPL@/aes \ -I$(srcdir)/../@CRYPTO_IMPL@/arcfour \ -I$(srcdir)/../@CRYPTO_IMPL@/sha1 \ -I$(srcdir)/../@CRYPTO_IMPL@/md4 \ - -I$(srcdir)/../@CRYPTO_IMPL@/md5 + -I$(srcdir)/../@CRYPTO_IMPL@/md5 \ + -I$(srcdir)/../@CRYPTO_IMPL@/enc_provider PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) DEFS= @@ -23,8 +24,8 @@ DEFS= ##DOSOBJFILEDEP =$(OUTPRE)crypto.lst $(OUTPRE)des.lst $(OUTPRE)md4.lst $(OUTPRE)md5.lst $(OUTPRE)sha1.lst $(OUTPRE)arcfour.lst $(OUTPRE)crc32.lst $(OUTPRE)dk.lst $(OUTPRE)old.lst $(OUTPRE)raw.lst $(OUTPRE)enc_prov.lst $(OUTPRE)hash_pro.lst $(OUTPRE)kh_pro.lst $(OUTPRE)aes.lst STLIBOBJS=\ - hmac.o \ - pbkdf2.o + ../@CRYPTO_IMPL@/hmac.o \ + ../@CRYPTO_IMPL@/pbkdf2.o OBJS=\ $(OUTPRE)../@CRYPTO_IMPL@/hmac.$(OBJEXT) \ @@ -34,16 +35,18 @@ SRCS=\ $(srcdir)/../@CRYPTO_IMPL@/hmac.c \ $(srcdir)/../@CRYPTO_IMPL@/pbkdf2.c -STOBJLISTS= ../@CRYPTO_IMPL@/des/OBJS.ST ../@CRYPTO_IMPL@/md4/OBJS.ST \ - ../@CRYPTO_IMPL@/md5/OBJS.ST ../@CRYPTO_IMPL@/sha1/OBJS.ST \ - ../@CRYPTO_IMPL@/arcfour/OBJS.ST \ - ../@CRYPTO_IMPL@/aes/OBJS.ST \ - ../@CRYPTO_IMPL@/OBJS.ST +STOBJLISTS= des/OBJS.ST md4/OBJS.ST \ + md5/OBJS.ST sha1/OBJS.ST \ + enc_provider/OBJS.ST \ + arcfour/OBJS.ST \ + aes/OBJS.ST \ + OBJS.ST -SUBDIROBJLISTS= ../@CRYPTO_IMPL@/des/OBJS.ST ../@CRYPTO_IMPL@/md4/OBJS.ST \ - ../@CRYPTO_IMPL@/md5/OBJS.ST ../@CRYPTO_IMPL@/sha1/OBJS.ST \ - ../@CRYPTO_IMPL@/arcfour/OBJS.ST \ - ../@CRYPTO_IMPL@/aes/OBJS.ST ../@CRYPTO_IMPL@/OBJS.ST +SUBDIROBJLISTS= des/OBJS.ST md4/OBJS.ST \ + md5/OBJS.ST sha1/OBJS.ST \ + enc_provider/OBJS.ST \ + arcfour/OBJS.ST \ + aes/OBJS.ST OBJS.ST ##DOS##LIBOBJS = $(OBJS) @@ -67,6 +70,9 @@ all-windows:: cd ..\sha1 @echo Making in crypto\sha1 $(MAKE) -$(MFLAGS) + cd ..\enc_provider + @echo Making in crypto\enc_provider + $(MAKE) -$(MFLAGS) cd ..\arcfour @echo Making in crypto\arcfour $(MAKE) -$(MFLAGS) @@ -88,6 +94,9 @@ clean-windows:: cd ..\sha1 @echo Making clean in crypto\sha1 $(MAKE) -$(MFLAGS) clean + cd ..\enc_provider + @echo Making clean in crypto\enc_provider + $(MAKE) -$(MFLAGS) clean cd ..\arcfour @echo Making clean in crypto\arcfour $(MAKE) -$(MFLAGS) clean @@ -109,6 +118,9 @@ check-windows:: cd ..\sha1 @echo Making check in crypto\sha1 $(MAKE) -$(MFLAGS) check + cd ..\enc_provider + @echo Making check in crypto\enc_provider + $(MAKE) -$(MFLAGS) check cd ..\arcfour @echo Making check in crypto\arcfour $(MAKE) -$(MFLAGS) check diff --git a/src/lib/crypto/builtin/aes/Makefile.in b/src/lib/crypto/builtin/aes/Makefile.in index ed36f7e613..49bc6a9c23 100644 --- a/src/lib/crypto/builtin/aes/Makefile.in +++ b/src/lib/crypto/builtin/aes/Makefile.in @@ -12,28 +12,30 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) +CIMPL = @CRYPTO_IMPL@/aes + STLIBOBJS=\ - aescrypt.o \ - aestab.o \ - aeskey.o \ - aes_s2k.o + ../../$(CIMPL)/aescrypt.o \ + ../../$(CIMPL)/aestab.o \ + ../../$(CIMPL)/aeskey.o \ + ../../$(CIMPL)/aes_s2k.o OBJS=\ - $(OUTPRE)aescrypt.$(OBJEXT) \ - $(OUTPRE)aestab.$(OBJEXT) \ - $(OUTPRE)aeskey.$(OBJEXT) \ - $(OUTPRE)aes_s2k.$(OBJEXT) + $(OUTPRE)../../$(CIMPL)/aescrypt.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/aestab.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/aeskey.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/aes_s2k.$(OBJEXT) SRCS=\ - $(srcdir)/aescrypt.c \ - $(srcdir)/aestab.c \ - $(srcdir)/aeskey.c \ - $(srcdir)/aes_s2k.c + $(srcdir)..//../$(CIMPL)/aescrypt.c \ + $(srcdir)..//../$(CIMPL)/aestab.c \ + $(srcdir)/../../$(CIMPL)/aeskey.c \ + $(srcdir)/../../$(CIMPL)/aes_s2k.c GEN_OBJS=\ - $(OUTPRE)aescrypt.$(OBJEXT) \ - $(OUTPRE)aestab.$(OBJEXT) \ - $(OUTPRE)aeskey.$(OBJEXT) + $(OUTPRE)../../$(CIMPL)/aescrypt.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/aestab.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/aeskey.$(OBJEXT) ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/builtin/arcfour/Makefile.in b/src/lib/crypto/builtin/arcfour/Makefile.in index cf6c511538..499c0a0765 100644 --- a/src/lib/crypto/builtin/arcfour/Makefile.in +++ b/src/lib/crypto/builtin/arcfour/Makefile.in @@ -12,20 +12,22 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) +CIMPL = @CRYPTO_IMPL@/arcfour + STLIBOBJS=\ - arcfour.o \ - arcfour_aead.o \ - arcfour_s2k.o + ../../$(CIMPL)/arcfour.o \ + ../../$(CIMPL)/arcfour_aead.o \ + ../../$(CIMPL)/arcfour_s2k.o OBJS=\ - $(OUTPRE)arcfour.$(OBJEXT) \ - $(OUTPRE)arcfour_aead.$(OBJEXT) \ - $(OUTPRE)arcfour_s2k.$(OBJEXT) + $(OUTPRE)../../$(CIMPL)/arcfour.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/arcfour_aead.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/arcfour_s2k.$(OBJEXT) SRCS=\ - $(srcdir)/arcfour.c \ - $(srcdir)/arcfour_aead.c\ - $(srcdir)/arcfour_s2k.c + $(srcdir)/../../$(CIMPL)/arcfour.c \ + $(srcdir)/../../$(CIMPL)/arcfour_aead.c\ + $(srcdir)/../../$(CIMPL)/arcfour_s2k.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/builtin/des/Makefile.in b/src/lib/crypto/builtin/des/Makefile.in index a609c42991..47e9b1a614 100644 --- a/src/lib/crypto/builtin/des/Makefile.in +++ b/src/lib/crypto/builtin/des/Makefile.in @@ -12,51 +12,53 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) +CIMPL = @CRYPTO_IMPL@/des + STLIBOBJS=\ - afsstring2key.o \ - d3_cbc.o \ - d3_aead.o \ - d3_kysched.o \ - des_prf.o \ - f_aead.o \ - f_cbc.o \ - f_cksum.o \ - f_parity.o \ - f_sched.o \ - f_tables.o \ - key_sched.o \ - string2key.o \ - weak_key.o + ../../$(CIMPL)/afsstring2key.o \ + ../../$(CIMPL)/d3_cbc.o \ + ../../$(CIMPL)/d3_aead.o \ + ../../$(CIMPL)/d3_kysched.o \ + ../../$(CIMPL)/des_prf.o \ + ../../$(CIMPL)/f_aead.o \ + ../../$(CIMPL)/f_cbc.o \ + ../../$(CIMPL)/f_cksum.o \ + ../../$(CIMPL)/f_parity.o \ + ../../$(CIMPL)/f_sched.o \ + ../../$(CIMPL)/f_tables.o \ + ../../$(CIMPL)/key_sched.o \ + ../../$(CIMPL)/string2key.o \ + ../../$(CIMPL)/weak_key.o -OBJS= $(OUTPRE)afsstring2key.$(OBJEXT) \ - $(OUTPRE)d3_cbc.$(OBJEXT) \ - $(OUTPRE)d3_aead.$(OBJEXT) \ - $(OUTPRE)d3_kysched.$(OBJEXT) \ - $(OUTPRE)des_prf.$(OBJEXT) \ - $(OUTPRE)f_aead.$(OBJEXT) \ - $(OUTPRE)f_cbc.$(OBJEXT) \ - $(OUTPRE)f_cksum.$(OBJEXT) \ - $(OUTPRE)f_parity.$(OBJEXT) \ - $(OUTPRE)f_sched.$(OBJEXT) \ - $(OUTPRE)f_tables.$(OBJEXT) \ - $(OUTPRE)key_sched.$(OBJEXT) \ - $(OUTPRE)string2key.$(OBJEXT) \ - $(OUTPRE)weak_key.$(OBJEXT) +OBJS= $(OUTPRE)../../$(CIMPL)/afsstring2key.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/d3_cbc.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/d3_aead.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/d3_kysched.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/des_prf.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_aead.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_cbc.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_cksum.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_parity.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_sched.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/f_tables.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/key_sched.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/string2key.$(OBJEXT) \ + $(OUTPRE)../../$(CIMPL)/weak_key.$(OBJEXT) -SRCS= $(srcdir)/afsstring2key.c \ - $(srcdir)/d3_cbc.c \ - $(srcdir)/d3_aead.c \ - $(srcdir)/d3_kysched.c \ - $(srcdir)/des_prf.c \ - $(srcdir)/f_aead.c \ - $(srcdir)/f_cbc.c \ - $(srcdir)/f_cksum.c \ - $(srcdir)/f_parity.c \ - $(srcdir)/f_sched.c \ - $(srcdir)/f_tables.c \ - $(srcdir)/key_sched.c \ - $(srcdir)/weak_key.c \ - $(srcdir)/string2key.c +SRCS= $(srcdir)/../../$(CIMPL)/afsstring2key.c \ + $(srcdir)/../../$(CIMPL)/d3_cbc.c \ + $(srcdir)/../../$(CIMPL)/d3_aead.c \ + $(srcdir)/../../$(CIMPL)/d3_kysched.c \ + $(srcdir)/../../$(CIMPL)/des_prf.c \ + $(srcdir)/../../$(CIMPL)/f_aead.c \ + $(srcdir)/../../$(CIMPL)/f_cbc.c \ + $(srcdir)/../../$(CIMPL)/f_cksum.c \ + $(srcdir)/../../$(CIMPL)/f_parity.c \ + $(srcdir)/../../$(CIMPL)/f_sched.c \ + $(srcdir)/../../$(CIMPL)/f_tables.c \ + $(srcdir)/../../$(CIMPL)/key_sched.c \ + $(srcdir)/../../$(CIMPL)/weak_key.c \ + $(srcdir)/../../$(CIMPL)/string2key.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/builtin/enc_provider/Makefile.in b/src/lib/crypto/builtin/enc_provider/Makefile.in new file mode 100644 index 0000000000..1895b519dc --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/Makefile.in @@ -0,0 +1,48 @@ +thisconfigdir=../../../.. +myfulldir=lib/crypto/builtin/enc_provider +mydir=lib/crypto/builtin/enc_provider +BUILDTOP=$(REL)..$(S)..$(S)..$(S).. +LOCALINCLUDES = -I$(srcdir)/../../@CRYPTO_IMPL@/des \ + -I$(srcdir)/../../@CRYPTO_IMPL@/arcfour \ + -I$(srcdir)/../../@CRYPTO_IMPL@/aes \ + -I$(srcdir)/../../krb \ + -I$(srcdir)/.. -I$(srcdir)/../../@CRYPTO_IMPL@ +DEFS= + +##DOS##BUILDTOP = ..\..\..\.. +##DOS##PREFIXDIR=enc_provider +##DOS##OBJFILE=..\$(OUTPRE)enc_prov.lst + +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) + +STLIBOBJS= \ + ../../@CRYPTO_IMPL@/enc_provider/des.o \ + ../../@CRYPTO_IMPL@/enc_provider/des3.o \ + ../../@CRYPTO_IMPL@/enc_provider/rc4.o \ + ../../@CRYPTO_IMPL@/enc_provider/aes.o + +OBJS= \ + $(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/des.$(OBJEXT) \ + $(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/des3.$(OBJEXT) \ + $(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/aes.$(OBJEXT) \ + $(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/rc4.$(OBJEXT) + +SRCS= \ + $(srcdir)/../../@CRYPTO_IMPL@/enc_provider/des.c \ + $(srcdir)/../../@CRYPTO_IMPL@/enc_provider/des3.c \ + $(srcdir)/../../@CRYPTO_IMPL@/enc_provider/aes.c \ + $(srcdir)/../../@CRYPTO_IMPL@/enc_provider/rc4.c + +##DOS##LIBOBJS = $(OBJS) + +all-unix:: all-libobjs + +includes:: depend + +depend:: $(SRCS) + +clean-unix:: clean-libobjs + +@libobj_frag@ + diff --git a/src/lib/crypto/builtin/enc_provider/aes.c b/src/lib/crypto/builtin/enc_provider/aes.c new file mode 100644 index 0000000000..88f2d9e350 --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/aes.c @@ -0,0 +1,415 @@ +/* + * lib/crypto/enc_provider/aes.c + * + * Copyright (C) 2003, 2007, 2008 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "enc_provider.h" +#include "aes.h" +#include + +#if 0 +aes_rval aes_blk_len(unsigned int blen, aes_ctx cx[1]); +aes_rval aes_enc_key(const unsigned char in_key[], unsigned int klen, aes_ctx cx[1]); +aes_rval aes_enc_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1]); +aes_rval aes_dec_key(const unsigned char in_key[], unsigned int klen, aes_ctx cx[1]); +aes_rval aes_dec_blk(const unsigned char in_blk[], unsigned char out_blk[], const aes_ctx cx[1]); +#endif + +#define CHECK_SIZES 0 + +#if 0 +static void printd (const char *descr, krb5_data *d) { + int i, j; + const int r = 16; + + printf("%s:", descr); + + for (i = 0; i < d->length; i += r) { + printf("\n %04x: ", i); + for (j = i; j < i + r && j < d->length; j++) + printf(" %02x", 0xff & d->data[j]); +#ifdef SHOW_TEXT + for (; j < i + r; j++) + printf(" "); + printf(" "); + for (j = i; j < i + r && j < d->length; j++) { + int c = 0xff & d->data[j]; + printf("%c", isprint(c) ? c : '.'); + } +#endif + } + printf("\n"); +} +#endif + +static inline void enc(char *out, const char *in, aes_ctx *ctx) +{ + if (aes_enc_blk((const unsigned char *)in, (unsigned char *)out, ctx) + != aes_good) + abort(); +} +static inline void dec(char *out, const char *in, aes_ctx *ctx) +{ + if (aes_dec_blk((const unsigned char *)in, (unsigned char *)out, ctx) + != aes_good) + abort(); +} + +static void xorblock(char *out, const char *in) +{ + int z; + for (z = 0; z < BLOCK_SIZE; z++) + out[z] ^= in[z]; +} + +krb5_error_code +krb5int_aes_encrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE]; + int nblocks = 0, blockno; + +/* CHECK_SIZES; */ + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + if (nblocks == 1) { + /* XXX Used for DK function. */ + enc(output->data, input->data, &ctx); + } else { + unsigned int nleft; + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + xorblock(tmp, input->data + blockno * BLOCK_SIZE); + enc(tmp2, tmp, &ctx); + memcpy(output->data + blockno * BLOCK_SIZE, tmp2, BLOCK_SIZE); + + /* Set up for next block. */ + memcpy(tmp, tmp2, BLOCK_SIZE); + } + /* Do final CTS step for last two blocks (the second of which + may or may not be incomplete). */ + xorblock(tmp, input->data + (nblocks - 2) * BLOCK_SIZE); + enc(tmp2, tmp, &ctx); + nleft = input->length - (nblocks - 1) * BLOCK_SIZE; + memcpy(output->data + (nblocks - 1) * BLOCK_SIZE, tmp2, nleft); + memcpy(tmp, tmp2, BLOCK_SIZE); + + memset(tmp3, 0, sizeof(tmp3)); + memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE, nleft); + xorblock(tmp, tmp3); + enc(tmp2, tmp, &ctx); + memcpy(output->data + (nblocks - 2) * BLOCK_SIZE, tmp2, BLOCK_SIZE); + if (ivec) + memcpy(ivec->data, tmp2, BLOCK_SIZE); + } + + return 0; +} + +krb5_error_code +krb5int_aes_decrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE]; + int nblocks = 0, blockno; + + CHECK_SIZES; + + if (aes_dec_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + nblocks = (input->length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + if (nblocks == 1) { + if (input->length < BLOCK_SIZE) + abort(); + dec(output->data, input->data, &ctx); + } else { + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + dec(tmp2, input->data + blockno * BLOCK_SIZE, &ctx); + xorblock(tmp2, tmp); + memcpy(output->data + blockno * BLOCK_SIZE, tmp2, BLOCK_SIZE); + memcpy(tmp, input->data + blockno * BLOCK_SIZE, BLOCK_SIZE); + } + /* Do last two blocks, the second of which (next-to-last block + of plaintext) may be incomplete. */ + dec(tmp2, input->data + (nblocks - 2) * BLOCK_SIZE, &ctx); + /* Set tmp3 to last ciphertext block, padded. */ + memset(tmp3, 0, sizeof(tmp3)); + memcpy(tmp3, input->data + (nblocks - 1) * BLOCK_SIZE, + input->length - (nblocks - 1) * BLOCK_SIZE); + /* Set tmp2 to last (possibly partial) plaintext block, and + save it. */ + xorblock(tmp2, tmp3); + memcpy(output->data + (nblocks - 1) * BLOCK_SIZE, tmp2, + input->length - (nblocks - 1) * BLOCK_SIZE); + /* Maybe keep the trailing part, and copy in the last + ciphertext block. */ + memcpy(tmp2, tmp3, input->length - (nblocks - 1) * BLOCK_SIZE); + /* Decrypt, to get next to last plaintext block xor previous + ciphertext. */ + dec(tmp3, tmp2, &ctx); + xorblock(tmp3, tmp); + memcpy(output->data + (nblocks - 2) * BLOCK_SIZE, tmp3, BLOCK_SIZE); + if (ivec) + memcpy(ivec->data, input->data + (nblocks - 2) * BLOCK_SIZE, + BLOCK_SIZE); + } + + return 0; +} + +static krb5_error_code +krb5int_aes_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE]; + int nblocks = 0, blockno; + size_t input_length, i; + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec != NULL) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + for (i = 0, input_length = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + assert(nblocks > 1); + + { + char blockN2[BLOCK_SIZE]; /* second last */ + char blockN1[BLOCK_SIZE]; /* last block */ + struct iov_block_state input_pos, output_pos; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + char blockN[BLOCK_SIZE]; + + krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos); + xorblock(tmp, blockN); + enc(tmp2, tmp, &ctx); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos); + + /* Set up for next block. */ + memcpy(tmp, tmp2, BLOCK_SIZE); + } + + /* Do final CTS step for last two blocks (the second of which + may or may not be incomplete). */ + + /* First, get the last two blocks */ + memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */ + krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos); + krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos); + + /* Encrypt second last block */ + xorblock(tmp, blockN2); + enc(tmp2, tmp, &ctx); + memcpy(blockN2, tmp2, BLOCK_SIZE); /* blockN2 now contains first block */ + memcpy(tmp, tmp2, BLOCK_SIZE); + + /* Encrypt last block */ + xorblock(tmp, blockN1); + enc(tmp2, tmp, &ctx); + memcpy(blockN1, tmp2, BLOCK_SIZE); + + /* Put the last two blocks back into the iovec (reverse order) */ + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos); + + if (ivec != NULL) + memcpy(ivec->data, blockN1, BLOCK_SIZE); + } + + return 0; +} + +static krb5_error_code +krb5int_aes_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE]; + int nblocks = 0, blockno; + unsigned int i; + size_t input_length; + + CHECK_SIZES; + + if (aes_dec_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec != NULL) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + for (i = 0, input_length = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + assert(nblocks > 1); + + { + char blockN2[BLOCK_SIZE]; /* second last */ + char blockN1[BLOCK_SIZE]; /* last block */ + struct iov_block_state input_pos, output_pos; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + char blockN[BLOCK_SIZE]; + + krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos); + dec(tmp2, blockN, &ctx); + xorblock(tmp2, tmp); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos); + memcpy(tmp, blockN, BLOCK_SIZE); + } + + /* Do last two blocks, the second of which (next-to-last block + of plaintext) may be incomplete. */ + + /* First, get the last two encrypted blocks */ + memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */ + krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos); + krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos); + + /* Decrypt second last block */ + dec(tmp2, blockN2, &ctx); + /* Set tmp2 to last (possibly partial) plaintext block, and + save it. */ + xorblock(tmp2, blockN1); + memcpy(blockN2, tmp2, BLOCK_SIZE); + + /* Maybe keep the trailing part, and copy in the last + ciphertext block. */ + input_length %= BLOCK_SIZE; + memcpy(tmp2, blockN1, input_length ? input_length : BLOCK_SIZE); + dec(tmp3, tmp2, &ctx); + xorblock(tmp3, tmp); + /* Copy out ivec first before we clobber blockN1 with plaintext */ + if (ivec != NULL) + memcpy(ivec->data, blockN1, BLOCK_SIZE); + memcpy(blockN1, tmp3, BLOCK_SIZE); + + /* Put the last two blocks back into the iovec */ + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos); + } + + return 0; +} + +static krb5_error_code +k5_aes_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != 16 && key->length != 32) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != key->length) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + + memcpy(key->contents, randombits->data, randombits->length); + return(0); +} + +static krb5_error_code +krb5int_aes_init_state (const krb5_keyblock *key, krb5_keyusage usage, + krb5_data *state) +{ + state->length = 16; + state->data = (void *) malloc(16); + if (state->data == NULL) + return ENOMEM; + memset(state->data, 0, state->length); + return 0; +} + +const struct krb5_enc_provider krb5int_enc_aes128 = { + 16, + 16, 16, + krb5int_aes_encrypt, + krb5int_aes_decrypt, + k5_aes_make_key, + krb5int_aes_init_state, + krb5int_default_free_state, + krb5int_aes_encrypt_iov, + krb5int_aes_decrypt_iov +}; + +const struct krb5_enc_provider krb5int_enc_aes256 = { + 16, + 32, 32, + krb5int_aes_encrypt, + krb5int_aes_decrypt, + k5_aes_make_key, + krb5int_aes_init_state, + krb5int_default_free_state, + krb5int_aes_encrypt_iov, + krb5int_aes_decrypt_iov +}; + diff --git a/src/lib/crypto/builtin/enc_provider/deps b/src/lib/crypto/builtin/enc_provider/deps new file mode 100644 index 0000000000..ed1b61cfc9 --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/deps @@ -0,0 +1,49 @@ +# +# Generated makefile dependencies follow. +# +des.so des.po $(OUTPRE)des.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \ + $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \ + $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ + $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ + $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ + $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ + $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + $(srcdir)/../des/des_int.h $(srcdir)/../../krb/aead.h \ + $(srcdir)/../../krb/cksumtypes.h des.c enc_provider.h +des3.so des3.po $(OUTPRE)des3.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \ + $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \ + $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ + $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ + $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ + $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ + $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + $(srcdir)/../des/des_int.h $(srcdir)/../../krb/aead.h \ + $(srcdir)/../../krb/cksumtypes.h des3.c +aes.so aes.po $(OUTPRE)aes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \ + $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \ + $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ + $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ + $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ + $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ + $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + $(srcdir)/../aes/aes.h $(srcdir)/../aes/uitypes.h \ + $(srcdir)/../../krb/aead.h $(srcdir)/../../krb/cksumtypes.h aes.c \ + enc_provider.h +rc4.so rc4.po $(OUTPRE)rc4.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \ + $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \ + $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ + $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ + $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ + $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ + $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + $(srcdir)/../arcfour/arcfour-int.h $(srcdir)/../arcfour/arcfour.h \ + $(srcdir)/../../krb/aead.h $(srcdir)/../../krb/cksumtypes.h enc_provider.h \ + rc4.c diff --git a/src/lib/crypto/builtin/enc_provider/des.c b/src/lib/crypto/builtin/enc_provider/des.c new file mode 100644 index 0000000000..547f6b976e --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/des.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * 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. + */ + +#include "k5-int.h" +#include "des_int.h" +#include "enc_provider.h" +#include "aead.h" + +static krb5_error_code +k5_des_docrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output, int enc) +{ + mit_des_key_schedule schedule; + + /* key->enctype was checked by the caller */ + + if (key->length != 8) + return(KRB5_BAD_KEYSIZE); + if ((input->length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + switch (mit_des_key_sched(key->contents, schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + + /* this has a return value, but the code always returns zero */ + + mit_des_cbc_encrypt((krb5_pointer) input->data, + (krb5_pointer) output->data, input->length, + schedule, + (ivec + ? (const unsigned char *) ivec->data + : (const unsigned char *) mit_des_zeroblock), + enc); + + memset(schedule, 0, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des_encrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + return(k5_des_docrypt(key, ivec, input, output, 1)); +} + +static krb5_error_code +k5_des_decrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + return(k5_des_docrypt(key, ivec, input, output, 0)); +} + +static krb5_error_code +k5_des_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != 8) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != 7) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + key->length = 8; + + /* take the seven bytes, move them around into the top 7 bits of the + 8 key bytes, then compute the parity bits */ + + memcpy(key->contents, randombits->data, randombits->length); + key->contents[7] = (((key->contents[0]&1)<<1) | ((key->contents[1]&1)<<2) | + ((key->contents[2]&1)<<3) | ((key->contents[3]&1)<<4) | + ((key->contents[4]&1)<<5) | ((key->contents[5]&1)<<6) | + ((key->contents[6]&1)<<7)); + + mit_des_fixup_key_parity(key->contents); + + return(0); +} + +static krb5_error_code +k5_des_docrypt_iov(const krb5_keyblock *key, const krb5_data *ivec, + krb5_crypto_iov *data, size_t num_data, int enc) +{ + mit_des_key_schedule schedule; + size_t input_length = 0; + unsigned int i; + + /* key->enctype was checked by the caller */ + + if (key->length != 8) + return(KRB5_BAD_KEYSIZE); + + for (i = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_DATA_IOV(iov)) + input_length += iov->data.length; + } + + if ((input_length % 8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + + switch (mit_des_key_sched(key->contents, schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + + /* this has a return value, but the code always returns zero */ + if (enc) + krb5int_des_cbc_encrypt_iov(data, num_data, schedule, ivec ? ivec->data : NULL); + else + krb5int_des_cbc_decrypt_iov(data, num_data, schedule, ivec ? ivec->data : NULL); + + memset(schedule, 0, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + return k5_des_docrypt_iov(key, ivec, data, num_data, 1); +} + +static krb5_error_code +k5_des_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + return k5_des_docrypt_iov(key, ivec, data, num_data, 0); +} + +const struct krb5_enc_provider krb5int_enc_des = { + 8, + 7, 8, + k5_des_encrypt, + k5_des_decrypt, + k5_des_make_key, + krb5int_des_init_state, + krb5int_default_free_state, + k5_des_encrypt_iov, + k5_des_decrypt_iov +}; diff --git a/src/lib/crypto/builtin/enc_provider/des3.c b/src/lib/crypto/builtin/enc_provider/des3.c new file mode 100644 index 0000000000..dc7c63338c --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/des3.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * 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. + */ + +#include "k5-int.h" +#include "des_int.h" +#include + +static krb5_error_code +validate_and_schedule(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, const krb5_data *output, + mit_des3_key_schedule *schedule) +{ + /* key->enctype was checked by the caller */ + + if (key->length != 24) + return(KRB5_BAD_KEYSIZE); + if ((input->length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + *schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + +static krb5_error_code +validate_and_schedule_iov(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_crypto_iov *data, size_t num_data, + mit_des3_key_schedule *schedule) +{ + size_t i, input_length; + + for (i = 0, input_length = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + if (key->length != 24) + return(KRB5_BAD_KEYSIZE); + if ((input_length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + *schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + +static krb5_error_code +k5_des3_encrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule(key, ivec, input, output, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_encrypt((krb5_pointer) input->data, + (krb5_pointer) output->data, input->length, + schedule[0], schedule[1], schedule[2], + ivec?(const unsigned char *) ivec->data:(const unsigned char *)mit_des_zeroblock); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des3_decrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule(key, ivec, input, output, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_decrypt((krb5_pointer) input->data, + (krb5_pointer) output->data, input->length, + schedule[0], schedule[1], schedule[2], + ivec?(const unsigned char *) ivec->data:(const unsigned char *)mit_des_zeroblock); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des3_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + int i; + + if (key->length != 24) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != 21) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + key->length = 24; + + /* take the seven bytes, move them around into the top 7 bits of the + 8 key bytes, then compute the parity bits. Do this three times. */ + + for (i=0; i<3; i++) { + memcpy(key->contents+i*8, randombits->data+i*7, 7); + key->contents[i*8+7] = (((key->contents[i*8]&1)<<1) | + ((key->contents[i*8+1]&1)<<2) | + ((key->contents[i*8+2]&1)<<3) | + ((key->contents[i*8+3]&1)<<4) | + ((key->contents[i*8+4]&1)<<5) | + ((key->contents[i*8+5]&1)<<6) | + ((key->contents[i*8+6]&1)<<7)); + + mit_des_fixup_key_parity(key->contents+i*8); + } + + return(0); +} + +static krb5_error_code +k5_des3_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_encrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des3_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_decrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +const struct krb5_enc_provider krb5int_enc_des3 = { + 8, + 21, 24, + k5_des3_encrypt, + k5_des3_decrypt, + k5_des3_make_key, + krb5int_des_init_state, + krb5int_default_free_state, + k5_des3_encrypt_iov, + k5_des3_decrypt_iov +}; + diff --git a/src/lib/crypto/builtin/enc_provider/enc_provider.h b/src/lib/crypto/builtin/enc_provider/enc_provider.h new file mode 100644 index 0000000000..92022b3c81 --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/enc_provider.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * 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. + */ + +#include "k5-int.h" + +extern const struct krb5_enc_provider krb5int_enc_des; +extern const struct krb5_enc_provider krb5int_enc_des3; +extern const struct krb5_enc_provider krb5int_enc_arcfour; +extern const struct krb5_enc_provider krb5int_enc_aes128; +extern const struct krb5_enc_provider krb5int_enc_aes256; +extern const struct krb5_enc_provider krb5int_enc_aes128_ctr; +extern const struct krb5_enc_provider krb5int_enc_aes256_ctr; + diff --git a/src/lib/crypto/builtin/enc_provider/rc4.c b/src/lib/crypto/builtin/enc_provider/rc4.c new file mode 100644 index 0000000000..d1dbb6cc3f --- /dev/null +++ b/src/lib/crypto/builtin/enc_provider/rc4.c @@ -0,0 +1,271 @@ +/* arcfour.c + * + * Copyright (c) 2000 by Computer Science Laboratory, + * Rensselaer Polytechnic Institute + * + * #include STD_DISCLAIMER + */ + +#include "k5-int.h" +#include "arcfour-int.h" +#include "enc_provider.h" +#include +/* gets the next byte from the PRNG */ +#if ((__GNUC__ >= 2) ) +static __inline__ unsigned int k5_arcfour_byte(ArcfourContext *); +#else +static unsigned int k5_arcfour_byte(ArcfourContext *); +#endif /* gcc inlines*/ + +/* Initializes the context and sets the key. */ +static krb5_error_code k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key, + unsigned int keylen); + +/* Encrypts/decrypts data. */ +static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest, + const unsigned char *src, unsigned int len); + +/* Interface layer to kerb5 crypto layer */ +static krb5_error_code +k5_arcfour_docrypt(const krb5_keyblock *, const krb5_data *, + const krb5_data *, krb5_data *); + +/* from a random bitstrem, construct a key */ +static krb5_error_code +k5_arcfour_make_key(const krb5_data *, krb5_keyblock *); + +static const unsigned char arcfour_weakkey1[] = {0x00, 0x00, 0xfd}; +static const unsigned char arcfour_weakkey2[] = {0x03, 0xfd, 0xfc}; +static const struct { + size_t length; + const unsigned char *data; +} arcfour_weakkeys[] = { + { sizeof (arcfour_weakkey1), arcfour_weakkey1}, + { sizeof (arcfour_weakkey2), arcfour_weakkey2}, +}; + +static inline unsigned int k5_arcfour_byte(ArcfourContext * ctx) +{ + unsigned int x; + unsigned int y; + unsigned int sx, sy; + unsigned char *state; + + state = ctx->state; + x = (ctx->x + 1) & 0xff; + sx = state[x]; + y = (sx + ctx->y) & 0xff; + sy = state[y]; + ctx->x = x; + ctx->y = y; + state[y] = sx; + state[x] = sy; + return state[(sx + sy) & 0xff]; +} + +static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest, + const unsigned char *src, unsigned int len) +{ + unsigned int i; + for (i = 0; i < len; i++) + dest[i] = src[i] ^ k5_arcfour_byte(ctx); +} + + +static krb5_error_code +k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key, + unsigned int key_len) +{ + unsigned int t, u; + unsigned int keyindex; + unsigned int stateindex; + unsigned char* state; + unsigned int counter; + + if (key_len != 16) + return KRB5_BAD_MSIZE; /*this is probably not the correct error code + to return */ + for (counter=0; + counter < sizeof(arcfour_weakkeys)/sizeof(arcfour_weakkeys[0]); + counter++) + if (!memcmp(key, arcfour_weakkeys[counter].data, + arcfour_weakkeys[counter].length)) + return KRB5DES_WEAK_KEY; /* most certainly not the correct error */ + + state = &ctx->state[0]; + ctx->x = 0; + ctx->y = 0; + for (counter = 0; counter < 256; counter++) + state[counter] = counter; + keyindex = 0; + stateindex = 0; + for (counter = 0; counter < 256; counter++) + { + t = state[counter]; + stateindex = (stateindex + key[keyindex] + t) & 0xff; + u = state[stateindex]; + state[stateindex] = t; + state[counter] = u; + if (++keyindex >= key_len) + keyindex = 0; + } + return 0; +} + + +/* The workhorse of the arcfour system, this impliments the cipher */ +static krb5_error_code +k5_arcfour_docrypt(const krb5_keyblock *key, const krb5_data *state, + const krb5_data *input, krb5_data *output) +{ + ArcfourContext *arcfour_ctx; + ArcFourCipherState *cipher_state; + int ret; + + if (key->length != 16) + return(KRB5_BAD_KEYSIZE); + if (state && (state->length != sizeof (ArcFourCipherState))) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + if (state) { + cipher_state = (ArcFourCipherState *) state->data; + arcfour_ctx=&cipher_state->ctx; + if (cipher_state->initialized == 0) { + if ((ret=k5_arcfour_init(arcfour_ctx, key->contents, key->length))) { + return ret; + } + cipher_state->initialized = 1; + } + k5_arcfour_crypt(arcfour_ctx, (unsigned char *) output->data, (const unsigned char *) input->data, input->length); + } + else { + arcfour_ctx=malloc(sizeof (ArcfourContext)); + if (arcfour_ctx == NULL) + return ENOMEM; + if ((ret=k5_arcfour_init(arcfour_ctx, key->contents, key->length))) { + free(arcfour_ctx); + return (ret); + } + k5_arcfour_crypt(arcfour_ctx, (unsigned char * ) output->data, + (const unsigned char * ) input->data, input->length); + memset(arcfour_ctx, 0, sizeof (ArcfourContext)); + free(arcfour_ctx); + } + + return 0; +} + +/* In-place encryption */ +static krb5_error_code +k5_arcfour_docrypt_iov(const krb5_keyblock *key, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + ArcfourContext *arcfour_ctx = NULL; + ArcFourCipherState *cipher_state = NULL; + krb5_error_code ret; + size_t i; + + if (key->length != 16) + return KRB5_BAD_KEYSIZE; + if (state != NULL && (state->length != sizeof(ArcFourCipherState))) + return KRB5_BAD_MSIZE; + + if (state != NULL) { + cipher_state = (ArcFourCipherState *)state->data; + arcfour_ctx = &cipher_state->ctx; + if (cipher_state->initialized == 0) { + ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length); + if (ret != 0) + return ret; + + cipher_state->initialized = 1; + } + } else { + arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext)); + if (arcfour_ctx == NULL) + return ENOMEM; + + ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length); + if (ret != 0) { + free(arcfour_ctx); + return ret; + } + } + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data, + (const unsigned char *)iov->data.data, iov->data.length); + } + + if (state == NULL) { + memset(arcfour_ctx, 0, sizeof(ArcfourContext)); + free(arcfour_ctx); + } + + return 0; +} + +static krb5_error_code +k5_arcfour_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != 16) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != 16) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + key->length = 16; + + memcpy(key->contents, randombits->data, randombits->length); + + return(0); +} + +static krb5_error_code +k5_arcfour_init_state (const krb5_keyblock *key, + krb5_keyusage keyusage, krb5_data *new_state) +{ + /* Note that we can't actually set up the state here because the key + * will change between now and when encrypt is called + * because it is data dependent. Yeah, this has strange + * properties. --SDH + */ + new_state->length = sizeof (ArcFourCipherState); + new_state->data = malloc (new_state->length); + if (new_state->data) { + memset (new_state->data, 0 , new_state->length); + /* That will set initialized to zero*/ + }else { + return (ENOMEM); + } + return 0; +} + +/* Since the arcfour cipher is identical going forwards and backwards, + we just call "docrypt" directly +*/ +const struct krb5_enc_provider krb5int_enc_arcfour = { + /* This seems to work... although I am not sure what the + implications are in other places in the kerberos library */ + 1, + /* Keysize is arbitrary in arcfour, but the constraints of the + system, and to attempt to work with the MSFT system forces us + to 16byte/128bit. Since there is no parity in the key, the + byte and length are the same. */ + 16, 16, + k5_arcfour_docrypt, + k5_arcfour_docrypt, + k5_arcfour_make_key, + k5_arcfour_init_state, /*xxx not implemented yet*/ + krb5int_default_free_state, + k5_arcfour_docrypt_iov, + k5_arcfour_docrypt_iov +}; + diff --git a/src/lib/crypto/builtin/md4/Makefile.in b/src/lib/crypto/builtin/md4/Makefile.in index 78dd0534b5..480906bc6b 100644 --- a/src/lib/crypto/builtin/md4/Makefile.in +++ b/src/lib/crypto/builtin/md4/Makefile.in @@ -12,11 +12,11 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= md4.o +STLIBOBJS= ../../@CRYPTO_IMPL@/md4/md4.o -OBJS= $(OUTPRE)md4.$(OBJEXT) +OBJS= $(OUTPRE)../../@CRYPTO_IMPL@/md4/md4.$(OBJEXT) -SRCS= $(srcdir)/md4.c +SRCS= $(srcdir)/../../@CRYPTO_IMPL@/md4/md4.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/builtin/md5/Makefile.in b/src/lib/crypto/builtin/md5/Makefile.in index 6da43749d4..9292919065 100644 --- a/src/lib/crypto/builtin/md5/Makefile.in +++ b/src/lib/crypto/builtin/md5/Makefile.in @@ -11,11 +11,11 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= md5.o +STLIBOBJS= ../../@CRYPTO_IMPL@/md5/md5.o -OBJS= $(OUTPRE)md5.$(OBJEXT) +OBJS= $(OUTPRE)../../@CRYPTO_IMPL@/md5/md5.$(OBJEXT) -SRCS= $(srcdir)/md5.c +SRCS= $(srcdir)/../../@CRYPTO_IMPL@/md5/md5.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/builtin/sha1/Makefile.in b/src/lib/crypto/builtin/sha1/Makefile.in index 81776f57bc..7610881881 100644 --- a/src/lib/crypto/builtin/sha1/Makefile.in +++ b/src/lib/crypto/builtin/sha1/Makefile.in @@ -11,11 +11,11 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= shs.o +STLIBOBJS= ../../@CRYPTO_IMPL@/sha1/shs.o -OBJS= $(OUTPRE)shs.$(OBJEXT) +OBJS= $(OUTPRE)../../@CRYPTO_IMPL@/sha1/shs.$(OBJEXT) -SRCS= $(srcdir)/shs.c +SRCS= $(srcdir)/../../@CRYPTO_IMPL@/sha1/shs.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/crypto_tests/Makefile.in b/src/lib/crypto/crypto_tests/Makefile.in index 4e33e83b8d..41704c85c2 100644 --- a/src/lib/crypto/crypto_tests/Makefile.in +++ b/src/lib/crypto/crypto_tests/Makefile.in @@ -2,7 +2,7 @@ thisconfigdir=../../.. myfulldir=lib/crypto/crypto_tests mydir=lib/crypto/crypto_tests BUILDTOP=$(REL)..$(S)..$(S).. -LOCALINCLUDES = -I$(srcdir)/../krb -I$(srcdir)/../krb/enc_provider \ +LOCALINCLUDES = -I$(srcdir)/../krb -I$(srcdir)/../@CRYPTO_IMPL@/enc_provider \ -I$(srcdir)/../krb/hash_provider -I$(srcdir)/../krb/keyhash_provider \ -I$(srcdir)/../krb/dk -I$(srcdir)/../@CRYPTO_IMPL@/ \ -I$(srcdir)/../krb/yarrow \ diff --git a/src/lib/crypto/krb/Makefile.in b/src/lib/crypto/krb/Makefile.in index 887fa160f0..636d2c6cdd 100644 --- a/src/lib/crypto/krb/Makefile.in +++ b/src/lib/crypto/krb/Makefile.in @@ -2,11 +2,11 @@ thisconfigdir=../../.. myfulldir=lib/crypto/krb mydir=lib/crypto/krb BUILDTOP=$(REL)..$(S)..$(S).. -SUBDIRS= crc32 dk enc_provider hash_provider keyhash_provider \ +SUBDIRS= crc32 dk hash_provider keyhash_provider \ old raw yarrow -LOCALINCLUDES = -I$(srcdir) -I$(srcdir)/enc_provider -I$(srcdir)/dk \ - -I$(srcdir)/hash_provider -I$(srcdir)/keyhash_provider \ - -I$(srcdir)/old -I$(srcdir)/raw -I$(srcdir)/yarrow \ +LOCALINCLUDES = -I$(srcdir) -I$(srcdir)/../@CRYPTO_IMPL@/enc_provider -I$(srcdir)/dk \ + -I$(srcdir)/hash_provider -I$(srcdir)/keyhash_provider \ + -I$(srcdir)/old -I$(srcdir)/raw -I$(srcdir)/yarrow \ -I$(srcdir)/../@CRYPTO_IMPL@/ -I$(srcdir)/../@CRYPTO_IMPL@/des \ -I$(srcdir)/../@CRYPTO_IMPL@/aes -I$(srcdir)/../@CRYPTO_IMPL@/arcfour \ -I$(srcdir)/../@CRYPTO_IMPL@/sha1 @@ -149,11 +149,11 @@ SRCS=\ $(srcdir)/verify_checksum.c \ $(srcdir)/verify_checksum_iov.c -STOBJLISTS=crc32/OBJS.ST dk/OBJS.ST enc_provider/OBJS.ST \ +STOBJLISTS=crc32/OBJS.ST dk/OBJS.ST \ hash_provider/OBJS.ST keyhash_provider/OBJS.ST \ old/OBJS.ST raw/OBJS.ST yarrow/OBJS.ST OBJS.ST -SUBDIROBJLISTS=crc32/OBJS.ST dk/OBJS.ST enc_provider/OBJS.ST \ +SUBDIROBJLISTS=crc32/OBJS.ST dk/OBJS.ST \ hash_provider/OBJS.ST keyhash_provider/OBJS.ST \ old/OBJS.ST raw/OBJS.ST yarrow/OBJS.ST OBJS.ST @@ -173,9 +173,6 @@ all-windows:: cd ..\dk @echo Making in crypto\dk $(MAKE) -$(MFLAGS) - cd ..\enc_provider - @echo Making in crypto\enc_provider - $(MAKE) -$(MFLAGS) cd ..\hash_provider @echo Making in crypto\hash_provider $(MAKE) -$(MFLAGS) @@ -200,9 +197,6 @@ clean-windows:: cd ..\dk @echo Making clean in crypto\dk $(MAKE) -$(MFLAGS) clean - cd ..\enc_provider - @echo Making clean in crypto\enc_provider - $(MAKE) -$(MFLAGS) clean cd ..\hash_provider @echo Making clean in crypto\hash_provider $(MAKE) -$(MFLAGS) clean @@ -227,9 +221,6 @@ check-windows:: cd ..\dk @echo Making check in crypto\dk $(MAKE) -$(MFLAGS) check - cd ..\enc_provider - @echo Making check in crypto\enc_provider - $(MAKE) -$(MFLAGS) check cd ..\hash_provider @echo Making check in crypto\hash_provider $(MAKE) -$(MFLAGS) check diff --git a/src/lib/crypto/krb/deps b/src/lib/crypto/krb/deps index fa65836789..58a614bdf4 100644 --- a/src/lib/crypto/krb/deps +++ b/src/lib/crypto/krb/deps @@ -192,7 +192,7 @@ etypes.so etypes.po $(OUTPRE)etypes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ $(srcdir)/../builtin/aes/aes_s2k.h $(srcdir)/../builtin/arcfour/arcfour.h \ $(srcdir)/../builtin/des/des_int.h $(srcdir)/dk/dk.h \ - $(srcdir)/enc_provider/enc_provider.h $(srcdir)/hash_provider/hash_provider.h \ + $(srcdir)/../builtin/enc_provider/enc_provider.h $(srcdir)/hash_provider/hash_provider.h \ $(srcdir)/old/old.h $(srcdir)/raw/raw.h etypes.c etypes.h keyblocks.so keyblocks.po $(OUTPRE)keyblocks.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ @@ -326,7 +326,7 @@ prng.so prng.po $(OUTPRE)prng.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ - $(srcdir)/../builtin/sha1/shs.h $(srcdir)/enc_provider/enc_provider.h \ + $(srcdir)/../builtin/sha1/shs.h $(srcdir)/../builtin/enc_provider/enc_provider.h \ $(srcdir)/yarrow/yarrow.h $(srcdir)/yarrow/ycipher.h \ $(srcdir)/yarrow/yhash.h $(srcdir)/yarrow/ytypes.h \ prng.c diff --git a/src/lib/crypto/krb/hash_provider/hash_crc32.c b/src/lib/crypto/krb/hash_provider/hash_crc32.c index ca26810676..780e1589dd 100644 --- a/src/lib/crypto/krb/hash_provider/hash_crc32.c +++ b/src/lib/crypto/krb/hash_provider/hash_crc32.c @@ -49,6 +49,7 @@ k5_crc32_hash(unsigned int icount, const krb5_data *input, } const struct krb5_hash_provider krb5int_hash_crc32 = { + "CRC32", CRC32_CKSUM_LENGTH, 1, k5_crc32_hash diff --git a/src/lib/crypto/krb/hash_provider/hash_md4.c b/src/lib/crypto/krb/hash_provider/hash_md4.c index 1fa23c214e..f507aaaf7d 100644 --- a/src/lib/crypto/krb/hash_provider/hash_md4.c +++ b/src/lib/crypto/krb/hash_provider/hash_md4.c @@ -49,6 +49,7 @@ k5_md4_hash(unsigned int icount, const krb5_data *input, } const struct krb5_hash_provider krb5int_hash_md4 = { + "MD4", RSA_MD4_CKSUM_LENGTH, 64, k5_md4_hash diff --git a/src/lib/crypto/krb/hash_provider/hash_md5.c b/src/lib/crypto/krb/hash_provider/hash_md5.c index 174c432a40..a6e380ae2d 100644 --- a/src/lib/crypto/krb/hash_provider/hash_md5.c +++ b/src/lib/crypto/krb/hash_provider/hash_md5.c @@ -49,6 +49,7 @@ k5_md5_hash(unsigned int icount, const krb5_data *input, } const struct krb5_hash_provider krb5int_hash_md5 = { + "MD5", RSA_MD5_CKSUM_LENGTH, 64, k5_md5_hash diff --git a/src/lib/crypto/krb/hash_provider/hash_sha1.c b/src/lib/crypto/krb/hash_provider/hash_sha1.c index ffc073cf14..00ab72bda6 100644 --- a/src/lib/crypto/krb/hash_provider/hash_sha1.c +++ b/src/lib/crypto/krb/hash_provider/hash_sha1.c @@ -51,6 +51,7 @@ k5_sha1_hash(unsigned int icount, const krb5_data *input, } const struct krb5_hash_provider krb5int_hash_sha1 = { + "SHA1", SHS_DIGESTSIZE, SHS_DATASIZE, k5_sha1_hash diff --git a/src/lib/crypto/krb/yarrow/Makefile.in b/src/lib/crypto/krb/yarrow/Makefile.in index d7f01e4692..b246c6cc05 100644 --- a/src/lib/crypto/krb/yarrow/Makefile.in +++ b/src/lib/crypto/krb/yarrow/Makefile.in @@ -2,7 +2,7 @@ thisconfigdir=../../../.. myfulldir=lib/crypto/krb/yarrow mydir=lib/crypto/krb/yarrow BUILDTOP=$(REL)..$(S)..$(S)..$(S).. -LOCALINCLUDES = -I$(srcdir)/.. -I$(srcdir)/../../@CRYPTO_IMPL@ -I$(srcdir)/../../@CRYPTO_IMPL@/sha1 -I$(srcdir)/../enc_provider +LOCALINCLUDES = -I$(srcdir)/.. -I$(srcdir)/../../@CRYPTO_IMPL@ -I$(srcdir)/../../@CRYPTO_IMPL@/sha1 -I$(srcdir)/../../@CRYPTO_IMPL@/enc_provider DEFS= ##DOS##BUILDTOP = ..\..\..\.. diff --git a/src/lib/crypto/krb/yarrow/deps b/src/lib/crypto/krb/yarrow/deps index 8d69431f61..ed10e31373 100644 --- a/src/lib/crypto/krb/yarrow/deps +++ b/src/lib/crypto/krb/yarrow/deps @@ -21,5 +21,5 @@ ycipher.so ycipher.po $(OUTPRE)ycipher.$(OBJEXT): $(BUILDTOP)/include/autoconf.h $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ - $(srcdir)/../../builtin/sha1/shs.h $(srcdir)/../enc_provider/enc_provider.h \ + $(srcdir)/../../builtin/sha1/shs.h $(srcdir)/../../builtin/enc_provider/enc_provider.h \ yarrow.h ycipher.c ycipher.h yhash.h ytypes.h diff --git a/src/lib/crypto/openssl/enc_provider/deps b/src/lib/crypto/openssl/enc_provider/deps new file mode 100644 index 0000000000..1d4dcbe975 --- /dev/null +++ b/src/lib/crypto/openssl/enc_provider/deps @@ -0,0 +1,50 @@ +# +# Generated makefile dependencies follow. +# +des.so des.po $(OUTPRE)des.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h $(srcdir)/../../builtin/des/des_int.h \ + $(srcdir)/../../krb/aead.h $(srcdir)/../cksumtypes.h des.c \ + enc_provider.h +des3.so des3.po $(OUTPRE)des3.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h $(srcdir)/../../builtin/des/des_int.h \ + $(srcdir)/../../krb/aead.h $(srcdir)/../cksumtypes.h des3.c +aes.so aes.po $(OUTPRE)aes.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h $(srcdir)/../../builtin/aes/aes.h \ + $(srcdir)/../../builtin/aes/uitypes.h $(srcdir)/../../krb/aead.h \ + $(srcdir)/../cksumtypes.h aes.c enc_provider.h +rc4.so rc4.po $(OUTPRE)rc4.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h $(srcdir)/../../builtin/arcfour/arcfour-int.h \ + $(srcdir)/../../builtin/arcfour/arcfour.h $(srcdir)/../../krb/aead.h \ + $(srcdir)/../cksumtypes.h enc_provider.h rc4.c diff --git a/src/lib/crypto/openssl/enc_provider/des.c b/src/lib/crypto/openssl/enc_provider/des.c new file mode 100644 index 0000000000..bc4313659b --- /dev/null +++ b/src/lib/crypto/openssl/enc_provider/des.c @@ -0,0 +1,271 @@ +/* + */ + +#include "k5-int.h" +#include "des_int.h" +#include "enc_provider.h" +#include +#include + +#define DES_BLOCK_SIZE 8 +#define DES_KEY_BYTES 7 +#define DES_KEY_LEN 8 + +static krb5_error_code +k5_des_encrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + int ret = 0, tmp_len = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + if (key->length != DES_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if ((input->length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + if ( ivec && ivec->data ) { + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + tmp_buf=OPENSSL_malloc(output->length); + if (!tmp_buf) + return ENOMEM; + memset(tmp_buf,0,output->length); + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_cbc(), NULL, keybuf, + (ivec && ivec->data) ? iv : NULL); + if (ret) { + EVP_CIPHER_CTX_set_padding(&ciph_ctx,0); + ret = EVP_EncryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)input->data, input->length); + if (ret) { + output->length = tmp_len; + ret = EVP_EncryptFinal_ex(&ciph_ctx, tmp_buf + tmp_len, &tmp_len); + } + } + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (ret) + memcpy(output->data,tmp_buf, output->length); + + memset(tmp_buf,0,output->length); + OPENSSL_free(tmp_buf); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; +} + +static krb5_error_code +k5_des_decrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + /* key->enctype was checked by the caller */ + int ret = 0, tmp_len = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL; + unsigned char *tmp_buf; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + if (key->length != DES_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if ((input->length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + if ( ivec != NULL && ivec->data ){ + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + tmp_buf=OPENSSL_malloc(output->length); + if (!tmp_buf) + return ENOMEM; + memset(tmp_buf,0,output->length); + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_DecryptInit_ex(&ciph_ctx, EVP_des_cbc(), NULL, keybuf, + (ivec && ivec->data) ? iv : NULL); + if (ret) { + EVP_CIPHER_CTX_set_padding(&ciph_ctx,0); + ret = EVP_DecryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)input->data, input->length); + if (ret) { + output->length = tmp_len; + ret = EVP_DecryptFinal_ex(&ciph_ctx, tmp_buf+tmp_len, &tmp_len); + } + } + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (ret) + memcpy(output->data,tmp_buf, output->length); + + memset(tmp_buf,0,output->length ); + OPENSSL_free(tmp_buf); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; +} + +static krb5_error_code +k5_des_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != DES_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != 7) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + + /* take the seven bytes, move them around into the top 7 bits of the + 8 key bytes, then compute the parity bits */ + + memcpy(key->contents, randombits->data, randombits->length); + key->contents[7] = (((key->contents[0]&1)<<1) | ((key->contents[1]&1)<<2) | + ((key->contents[2]&1)<<3) | ((key->contents[3]&1)<<4) | + ((key->contents[4]&1)<<5) | ((key->contents[5]&1)<<6) | + ((key->contents[6]&1)<<7)); + + mit_des_fixup_key_parity(key->contents); + + return(0); +} + +static krb5_error_code +k5_des_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + int ret = 0, tmp_len = 0; + unsigned int i = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL ; + krb5_crypto_iov *iov = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + if (ivec && ivec->data){ + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_cbc(), NULL, + keybuf, (ivec && ivec->data) ? iv : NULL); + if (!ret) + return KRB5_CRYPTO_INTERNAL; + + for (i = 0; i < num_data; i++) { + iov = &data[i]; + if (iov->data.length <= 0) break; + tmp_len = iov->data.length; + + if (ENCRYPT_DATA_IOV(iov)) { + tmp_buf=(unsigned char *)iov->data.data; + ret = EVP_EncryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)iov->data.data, iov->data.length); + if (!ret) break; + iov->data.length = tmp_len; + } + } + if(ret) + ret = EVP_EncryptFinal_ex(&ciph_ctx, (unsigned char *)tmp_buf, &tmp_len); + + if (ret) + iov->data.length += tmp_len; + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; + +} + +static krb5_error_code +k5_des_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + int ret = 0, tmp_len = 0; + unsigned int i = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL ; + krb5_crypto_iov *iov = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + if (ivec && ivec->data){ + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + ret = EVP_DecryptInit_ex(&ciph_ctx, EVP_des_cbc(), NULL, + keybuf, (ivec && ivec->data) ? iv : NULL); + if (!ret) + return KRB5_CRYPTO_INTERNAL; + + for (i = 0; i < num_data; i++) { + iov = &data[i]; + if (iov->data.length <= 0) break; + tmp_len = iov->data.length; + + if (ENCRYPT_DATA_IOV(iov)) { + tmp_buf=(unsigned char *)iov->data.data; + ret = EVP_DecryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)iov->data.data, iov->data.length); + if (!ret) break; + iov->data.length = tmp_len; + } + } + if(ret) + ret = EVP_DecryptFinal_ex(&ciph_ctx, (unsigned char *)tmp_buf, &tmp_len); + + if (ret) + iov->data.length += tmp_len; + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; +} + +const struct krb5_enc_provider krb5int_enc_des = { + DES_BLOCK_SIZE, + DES_KEY_BYTES, DES_KEY_LEN, + k5_des_encrypt, + k5_des_decrypt, + k5_des_make_key, + krb5int_des_init_state, + krb5int_default_free_state, + k5_des_encrypt_iov, + k5_des_decrypt_iov +}; + diff --git a/src/lib/crypto/openssl/enc_provider/des3.c b/src/lib/crypto/openssl/enc_provider/des3.c new file mode 100644 index 0000000000..1cc67483df --- /dev/null +++ b/src/lib/crypto/openssl/enc_provider/des3.c @@ -0,0 +1,352 @@ +/* + */ + +#include "k5-int.h" +#include "des_int.h" +#include +#include + + +#define DES_BLOCK_SIZE 8 +#define DES3_KEY_BYTES 21 +#define DES3_KEY_LEN 24 + +static krb5_error_code +validate(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, const krb5_data *output) +{ + mit_des3_key_schedule schedule; + + /* key->enctype was checked by the caller */ + + if (key->length != DES3_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if ((input->length%DES_BLOCK_SIZE) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + +static krb5_error_code +validate_iov(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_crypto_iov *data, size_t num_data) +{ + size_t i, input_length; + mit_des3_key_schedule schedule; + + for (i = 0, input_length = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + if (key->length != DES3_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if ((input_length%DES_BLOCK_SIZE) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + +static krb5_error_code +k5_des3_encrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + + int ret = 0, tmp_len = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + ret = validate(key, ivec, input, output); + if (ret) + return ret; + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + if (ivec && ivec->data) { + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + tmp_buf = OPENSSL_malloc(output->length); + if (!tmp_buf) + return ENOMEM; + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL, keybuf, + (ivec && ivec->data) ? iv : NULL); + if (ret) { + EVP_CIPHER_CTX_set_padding(&ciph_ctx,0); + ret = EVP_EncryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)input->data, input->length); + if (ret) { + output->length = tmp_len; + ret = EVP_EncryptFinal_ex(&ciph_ctx, tmp_buf+tmp_len, &tmp_len); + } + } + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (ret) + memcpy(output->data,tmp_buf, output->length); + memset(tmp_buf,0,output->length); + OPENSSL_free(tmp_buf); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; + +} + +static krb5_error_code +k5_des3_decrypt(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + int ret = 0, tmp_len = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + ret = validate(key, ivec, input, output); + if (ret) + return ret; + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + if (ivec && ivec->data) { + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + tmp_buf=OPENSSL_malloc(output->length); + if (!tmp_buf) + return ENOMEM; + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_DecryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL, keybuf, + (ivec && ivec->data) ? iv: NULL); + if (ret) { + EVP_CIPHER_CTX_set_padding(&ciph_ctx,0); + ret = EVP_DecryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)input->data, input->length); + if (ret) { + output->length = tmp_len; + ret = EVP_DecryptFinal_ex(&ciph_ctx, tmp_buf+tmp_len, &tmp_len); + } + } + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (ret) + memcpy(output->data,tmp_buf, output->length); + + memset(tmp_buf,0,output->length); + OPENSSL_free(tmp_buf); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; + +} + +static krb5_error_code +k5_des3_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + int i; + + if (key->length != DES3_KEY_LEN) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != DES3_KEY_BYTES) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + + /* take the seven bytes, move them around into the top 7 bits of the + 8 key bytes, then compute the parity bits. Do this three times. */ + + for (i=0; i<3; i++) { + memcpy(key->contents+i*8, randombits->data+i*7, 7); + key->contents[i*8+7] = (((key->contents[i*8]&1)<<1) | + ((key->contents[i*8+1]&1)<<2) | + ((key->contents[i*8+2]&1)<<3) | + ((key->contents[i*8+3]&1)<<4) | + ((key->contents[i*8+4]&1)<<5) | + ((key->contents[i*8+5]&1)<<6) | + ((key->contents[i*8+6]&1)<<7)); + + mit_des_fixup_key_parity(key->contents+i*8); + } + + return(0); +} + +static krb5_error_code +validate_and_schedule_iov(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_crypto_iov *data, size_t num_data, + mit_des3_key_schedule *schedule) +{ + size_t i, input_length; + + for (i = 0, input_length = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + if (key->length != 24) + return(KRB5_BAD_KEYSIZE); + if ((input_length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + *schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + +static krb5_error_code +k5_des3_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ +#if 0 + int ret = 0, tmp_len = 0; + unsigned int i = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL ; + krb5_crypto_iov *iov = NULL; + unsigned char *tmp_buf = NULL; + unsigned char iv[EVP_MAX_IV_LENGTH]; + + ret = validate_iov(key, ivec, data, num_data); + if (ret) + return ret; + + if (ivec && ivec->data){ + memset(iv,0,sizeof(iv)); + memcpy(iv,ivec->data,ivec->length); + } + + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL, + keybuf, (ivec && ivec->data) ? iv : NULL); + if (!ret) + return KRB5_CRYPTO_INTERNAL; + + for (i = 0; i < num_data; i++) { + iov = &data[i]; + if (iov->data.length <= 0) break; + tmp_len = iov->data.length; + + if (ENCRYPT_IOV(iov)) { + tmp_buf=(unsigned char *)iov->data.data; + ret = EVP_EncryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, + (unsigned char *)iov->data.data, iov->data.length); + if (!ret) break; + iov->data.length = tmp_len; + } + } + if(ret) + ret = EVP_EncryptFinal_ex(&ciph_ctx, (unsigned char *)tmp_buf, &tmp_len); + + if (ret) + iov->data.length += tmp_len; + + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; +#endif + +//#if 0 + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_encrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + return(0); +//#endif +} + +static krb5_error_code +k5_des3_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_decrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +const struct krb5_enc_provider krb5int_enc_des3 = { + DES_BLOCK_SIZE, + DES3_KEY_BYTES, DES3_KEY_LEN, + k5_des3_encrypt, + k5_des3_decrypt, + k5_des3_make_key, + krb5int_des_init_state, + krb5int_default_free_state, + k5_des3_encrypt_iov, + k5_des3_decrypt_iov +}; + diff --git a/src/lib/crypto/openssl/enc_provider/enc_provider.h b/src/lib/crypto/openssl/enc_provider/enc_provider.h new file mode 100644 index 0000000000..d46e1b4464 --- /dev/null +++ b/src/lib/crypto/openssl/enc_provider/enc_provider.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * 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. + */ + +#include "k5-int.h" + +extern const struct krb5_enc_provider krb5int_enc_des; +extern const struct krb5_enc_provider krb5int_enc_des3; +extern const struct krb5_enc_provider krb5int_enc_arcfour; +extern const struct krb5_enc_provider krb5int_enc_aes128; +extern const struct krb5_enc_provider krb5int_enc_aes256; +extern const struct krb5_enc_provider krb5int_enc_aes128_ctr; +extern const struct krb5_enc_provider krb5int_enc_aes256_ctr; + diff --git a/src/lib/crypto/openssl/enc_provider/rc4.c b/src/lib/crypto/openssl/enc_provider/rc4.c new file mode 100644 index 0000000000..b82af5247a --- /dev/null +++ b/src/lib/crypto/openssl/enc_provider/rc4.c @@ -0,0 +1,167 @@ +/* arcfour.c + * + * #include STD_DISCLAIMER + */ + +#include "k5-int.h" +#include "arcfour-int.h" +#include "enc_provider.h" +#include +#include + +#define RC4_KEY_SIZE 16 +#define RC4_BLOCK_SIZE 1 + +/* Interface layer to kerb5 crypto layer */ +static krb5_error_code +k5_arcfour_docrypt(const krb5_keyblock *, const krb5_data *, + const krb5_data *, krb5_data *); + +/* from a random bitstrem, construct a key */ +static krb5_error_code +k5_arcfour_make_key(const krb5_data *, krb5_keyblock *); + +static krb5_error_code +k5_arcfour_free_state ( krb5_data *state); +static krb5_error_code +k5_arcfour_init_state (const krb5_keyblock *key, + krb5_keyusage keyusage, krb5_data *new_state); + +/* The workhorse of the arcfour system, this impliments the cipher */ +static krb5_error_code +k5_arcfour_docrypt(const krb5_keyblock *key, const krb5_data *state, + const krb5_data *input, krb5_data *output) +{ + int ret = 0, tmp_len = 0; + unsigned char *keybuf = NULL; + unsigned char *tmp_buf = NULL; + EVP_CIPHER_CTX ciph_ctx; + + if (key->length != RC4_KEY_SIZE) + return(KRB5_BAD_KEYSIZE); + + if (input->length != output->length) + return(KRB5_BAD_MSIZE); + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + EVP_CIPHER_CTX_init(&ciph_ctx); + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_rc4(), NULL, keybuf, NULL); + if (ret) { + tmp_buf=(unsigned char *)output->data; + ret = EVP_EncryptUpdate(&ciph_ctx, tmp_buf, &tmp_len, (unsigned char *)input->data, input->length); + output->length = tmp_len; + } + if (ret) { + tmp_buf += tmp_len; + ret = EVP_EncryptFinal_ex(&ciph_ctx, tmp_buf, &tmp_len); + } + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + output->length += tmp_len; + + if (!ret) + return KRB5_CRYPTO_INTERNAL; + return 0; +} + + +/* In-place decryption */ +static krb5_error_code +k5_arcfour_docrypt_iov(const krb5_keyblock *key, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + size_t i; + int ret = 0, tmp_len = 0; + EVP_CIPHER_CTX ciph_ctx; + unsigned char *keybuf = NULL ; + krb5_crypto_iov *iov = NULL; + unsigned char *tmp_buf = NULL; + + keybuf=key->contents; + keybuf[key->length] = '\0'; + + EVP_CIPHER_CTX_init(&ciph_ctx); + + ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_rc4(), NULL, keybuf, NULL); + if (!ret) + return -1; + + for (i = 0; i < num_data; i++) { + iov = &data[i]; + if (iov->data.length <= 0) break; + tmp_len = iov->data.length; + + if (ENCRYPT_IOV(iov)) { + tmp_buf=(unsigned char *)iov->data.data; + ret = EVP_EncryptUpdate(&ciph_ctx, + tmp_buf, &tmp_len, + (unsigned char *)iov->data.data, iov->data.length); + if (!ret) break; + iov->data.length = tmp_len; + } + } + if(ret) + ret = EVP_EncryptFinal_ex(&ciph_ctx, (unsigned char *)tmp_buf, &tmp_len); + if (ret) + iov->data.length += tmp_len; + EVP_CIPHER_CTX_cleanup(&ciph_ctx); + + if (!ret) + return -1; + return 0; +} + + +static krb5_error_code +k5_arcfour_make_key(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != RC4_KEY_SIZE) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != RC4_KEY_SIZE) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + + memcpy(key->contents, randombits->data, randombits->length); + + return(0); +} + +static krb5_error_code +k5_arcfour_free_state ( krb5_data *state) +{ + return 0; /* not implemented */ +} + +static krb5_error_code +k5_arcfour_init_state (const krb5_keyblock *key, + krb5_keyusage keyusage, krb5_data *new_state) +{ + return 0; /* not implemented */ + +} + +/* Since the arcfour cipher is identical going forwards and backwards, + we just call "docrypt" directly +*/ +const struct krb5_enc_provider krb5int_enc_arcfour = { + /* This seems to work... although I am not sure what the + implications are in other places in the kerberos library */ + RC4_BLOCK_SIZE, + /* Keysize is arbitrary in arcfour, but the constraints of the + system, and to attempt to work with the MSFT system forces us + to 16byte/128bit. Since there is no parity in the key, the + byte and length are the same. */ + RC4_KEY_SIZE, RC4_KEY_SIZE, + k5_arcfour_docrypt, + k5_arcfour_docrypt, + k5_arcfour_make_key, + k5_arcfour_init_state, /*xxx not implemented */ + k5_arcfour_free_state, /*xxx not implemented */ + k5_arcfour_docrypt_iov, + k5_arcfour_docrypt_iov +}; + diff --git a/src/lib/crypto/openssl/hmac.c b/src/lib/crypto/openssl/hmac.c new file mode 100644 index 0000000000..a5543c9773 --- /dev/null +++ b/src/lib/crypto/openssl/hmac.c @@ -0,0 +1,110 @@ +/* + */ + +#include "k5-int.h" +#include "aead.h" +#include +#include + +/* + * the HMAC transform looks like: + * + * H(K XOR opad, H(K XOR ipad, text)) + * + * where H is a cryptographic hash + * K is an n byte key + * ipad is the byte 0x36 repeated blocksize times + * opad is the byte 0x5c repeated blocksize times + * and text is the data being protected + */ + +static const EVP_MD * +map_digest(const struct krb5_hash_provider *hash) +{ + if (!strncmp(hash->hash_name, "SHA1",4)) + return EVP_sha1(); + else if (!strncmp(hash->hash_name, "MD5", 3)) + return EVP_md5(); + else if (!strncmp(hash->hash_name, "MD4", 3)) + return EVP_md4(); + else + return NULL; +} + +krb5_error_code +krb5_hmac(const struct krb5_hash_provider *hash, const krb5_keyblock *key, + unsigned int icount, const krb5_data *input, krb5_data *output) +{ + unsigned int i = 0, md_len = 0; + unsigned char md[EVP_MAX_MD_SIZE]; + HMAC_CTX c; + size_t hashsize, blocksize; + + hashsize = hash->hashsize; + blocksize = hash->blocksize; + + if (key->length > blocksize) + return(KRB5_CRYPTO_INTERNAL); + if (output->length < hashsize) + return(KRB5_BAD_MSIZE); + /* if this isn't > 0, then there won't be enough space in this + array to compute the outer hash */ + if (icount == 0) + return(KRB5_CRYPTO_INTERNAL); + + if (!map_digest(hash)) + return(KRB5_CRYPTO_INTERNAL); // unsupported alg + + HMAC_CTX_init(&c); + HMAC_Init(&c, key->contents, key->length, map_digest(hash)); + for ( i = 0; i < icount; i++ ) { + HMAC_Update(&c,(const unsigned char*)input[i].data, input[i].length); + } + HMAC_Final(&c,(unsigned char *)md, &md_len); + if ( md_len <= output->length) { + output->length = md_len; + memcpy(output->data, md, output->length); + } + HMAC_CTX_cleanup(&c); + return 0; + + +} + +krb5_error_code +krb5int_hmac_iov(const struct krb5_hash_provider *hash, const krb5_keyblock *key, + const krb5_crypto_iov *data, size_t num_data, krb5_data *output) +{ + krb5_data *sign_data; + size_t num_sign_data; + krb5_error_code ret; + size_t i, j; + + /* Create a checksum over all the data to be signed */ + for (i = 0, num_sign_data = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + num_sign_data++; + } + + /* XXX cleanup to avoid alloc */ + sign_data = (krb5_data *)calloc(num_sign_data, sizeof(krb5_data)); + if (sign_data == NULL) + return ENOMEM; + + for (i = 0, j = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + sign_data[j++] = iov->data; + } + + /* caller must store checksum in iov as it may be TYPE_TRAILER or TYPE_CHECKSUM */ + ret = krb5_hmac(hash, key, num_sign_data, sign_data, output); + + free(sign_data); + + return ret; +} + diff --git a/src/lib/crypto/openssl/md4/deps b/src/lib/crypto/openssl/md4/deps new file mode 100644 index 0000000000..1decaf9715 --- /dev/null +++ b/src/lib/crypto/openssl/md4/deps @@ -0,0 +1,13 @@ +# +# Generated makefile dependencies follow. +# +md4.so md4.po $(OUTPRE)md4.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h md4.c rsa-md4.h diff --git a/src/lib/crypto/openssl/md4/md4.c b/src/lib/crypto/openssl/md4/md4.c new file mode 100644 index 0000000000..88d5191882 --- /dev/null +++ b/src/lib/crypto/openssl/md4/md4.c @@ -0,0 +1,29 @@ +/* + * lib/crypto/openssl/md4/md4.c + */ + +#include "k5-int.h" +#include "rsa-md4.h" +#include +#include + +void +krb5_MD4Init (krb5_MD4_CTX *mdContext) +{ + EVP_MD_CTX_init(&mdContext->ossl_md4_ctx ); + EVP_DigestInit_ex(&mdContext->ossl_md4_ctx, EVP_md4(), NULL); + +} +void +krb5_MD4Update (krb5_MD4_CTX *mdContext, const unsigned char *inBuf, unsigned int inLen) +{ + EVP_DigestUpdate(&mdContext->ossl_md4_ctx, inBuf, inLen); +} + +void +krb5_MD4Final (krb5_MD4_CTX *mdContext) +{ + EVP_DigestFinal_ex(&mdContext->ossl_md4_ctx, mdContext->digest , NULL); + EVP_MD_CTX_cleanup(&mdContext->ossl_md4_ctx ); +} + diff --git a/src/lib/crypto/openssl/md4/rsa-md4.h b/src/lib/crypto/openssl/md4/rsa-md4.h new file mode 100644 index 0000000000..4b02047276 --- /dev/null +++ b/src/lib/crypto/openssl/md4/rsa-md4.h @@ -0,0 +1,99 @@ +/* + * lib/crypto/md4/rsa-md4.h + * + * Copyright 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * RSA MD4 header file, with Kerberos/STDC additions. + */ + +#ifndef __KRB5_RSA_MD4_H__ +#define __KRB5_RSA_MD4_H__ + +#ifdef unicos61 +#include +#endif /* unicos61 */ + +#include +#include + +/* 16 u_char's in the digest */ +#define RSA_MD4_CKSUM_LENGTH 16 +/* des blocksize is 8, so this works nicely... */ +#define OLD_RSA_MD4_DES_CKSUM_LENGTH 16 +#define NEW_RSA_MD4_DES_CKSUM_LENGTH 24 +#define RSA_MD4_DES_CONFOUND_LENGTH 8 + +/* + ********************************************************************** + ** md4.h -- Header file for implementation of MD4 ** + ** RSA Data Security, Inc. MD4 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD4 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD4 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* Data structure for MD4 (Message Digest) computation */ +typedef struct { + EVP_MD_CTX ossl_md4_ctx; + krb5_int32 * digest_len; + krb5_ui_4 i[2]; /* number of _bits_ handled mod 2^64 */ + krb5_ui_4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD4Final call */ +} krb5_MD4_CTX; + +extern void krb5_MD4Init(krb5_MD4_CTX *); +extern void krb5_MD4Update(krb5_MD4_CTX *, const unsigned char *, unsigned int); +extern void krb5_MD4Final(krb5_MD4_CTX *); + +/* + ********************************************************************** + ** End of md4.h ** + ******************************* (cut) ******************************** + */ +#endif /* __KRB5_RSA_MD4_H__ */ diff --git a/src/lib/crypto/openssl/md5/deps b/src/lib/crypto/openssl/md5/deps new file mode 100644 index 0000000000..fc3378d2e7 --- /dev/null +++ b/src/lib/crypto/openssl/md5/deps @@ -0,0 +1,13 @@ +# +# Generated makefile dependencies follow. +# +md5.so md5.po $(OUTPRE)md5.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h md5.c rsa-md5.h diff --git a/src/lib/crypto/openssl/md5/md5.c b/src/lib/crypto/openssl/md5/md5.c new file mode 100644 index 0000000000..8519dd535e --- /dev/null +++ b/src/lib/crypto/openssl/md5/md5.c @@ -0,0 +1,36 @@ + +#include "k5-int.h" +#include "rsa-md5.h" +#include +#include + +/* The routine krb5_MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +krb5_MD5Init (krb5_MD5_CTX *mdContext) +{ + EVP_MD_CTX_init(&mdContext->ossl_md5_ctx); + EVP_DigestInit_ex(&mdContext->ossl_md5_ctx, EVP_md5(), NULL); +} + +/* The routine krb5_MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +krb5_MD5Update (krb5_MD5_CTX *mdContext, const unsigned char *inBuf, unsigned int inLen) +{ + EVP_DigestUpdate(&mdContext->ossl_md5_ctx, inBuf, inLen); +} + +/* The routine krb5_MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +krb5_MD5Final (krb5_MD5_CTX *mdContext) +{ + EVP_DigestFinal_ex(&mdContext->ossl_md5_ctx, mdContext->digest, NULL); + EVP_MD_CTX_cleanup(&mdContext->ossl_md5_ctx); +} + diff --git a/src/lib/crypto/openssl/md5/rsa-md5.h b/src/lib/crypto/openssl/md5/rsa-md5.h new file mode 100644 index 0000000000..7240b20ca0 --- /dev/null +++ b/src/lib/crypto/openssl/md5/rsa-md5.h @@ -0,0 +1,27 @@ + +#ifndef KRB5_RSA_MD5__ +#define KRB5_RSA_MD5__ + +#include +#include + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + EVP_MD_CTX ossl_md5_ctx; + krb5_int32 * digest_len; + krb5_ui_4 i[2]; /* number of _bits_ handled mod 2^64 */ + krb5_ui_4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} krb5_MD5_CTX; + +extern void krb5_MD5Init(krb5_MD5_CTX *); +extern void krb5_MD5Update(krb5_MD5_CTX *,const unsigned char *,unsigned int); +extern void krb5_MD5Final(krb5_MD5_CTX *); + +#define RSA_MD5_CKSUM_LENGTH 16 +#define OLD_RSA_MD5_DES_CKSUM_LENGTH 16 +#define NEW_RSA_MD5_DES_CKSUM_LENGTH 24 +#define RSA_MD5_DES_CONFOUND_LENGTH 8 + +#endif /* KRB5_RSA_MD5__ */ diff --git a/src/lib/crypto/openssl/pbkdf2.c b/src/lib/crypto/openssl/pbkdf2.c new file mode 100644 index 0000000000..bef8be2e9c --- /dev/null +++ b/src/lib/crypto/openssl/pbkdf2.c @@ -0,0 +1,53 @@ +/* + * lib/crypto/openssl/pbkdf2.c + * + * Copyright 2002, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Implementation of PBKDF2 from RFC 2898. + * Not currently used; likely to be used when we get around to AES support. + */ + +#include +#include "k5-int.h" +#include "hash_provider.h" + +#include +#include +#include + + +krb5_error_code +krb5int_pbkdf2_hmac_sha1 (const krb5_data *out, unsigned long count, + const krb5_data *pass, const krb5_data *salt) +{ +/* + * This is an implementation of PKCS#5 v2.0 + * Does not return an error + */ + PKCS5_PBKDF2_HMAC_SHA1(pass->data, pass->length, + (unsigned char *)salt->data, salt->length, count, + out->length, (unsigned char *)out->data); + return 0; +} + diff --git a/src/lib/crypto/openssl/sha1/deps b/src/lib/crypto/openssl/sha1/deps new file mode 100644 index 0000000000..a8f51a8e6b --- /dev/null +++ b/src/lib/crypto/openssl/sha1/deps @@ -0,0 +1,13 @@ +# +# Generated makefile dependencies follow. +# +shs.so shs.po $(OUTPRE)shs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/autoconf.h \ + $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ + $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ + $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ + $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ + $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ + $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ + $(SRCTOP)/include/socket-utils.h shs.c shs.h diff --git a/src/lib/crypto/openssl/sha1/shs.c b/src/lib/crypto/openssl/sha1/shs.c new file mode 100644 index 0000000000..9fb60f87c9 --- /dev/null +++ b/src/lib/crypto/openssl/sha1/shs.c @@ -0,0 +1,34 @@ +#include "shs.h" +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +/* Initialize the SHS values */ +void shsInit(SHS_INFO *shsInfo) +{ + EVP_MD_CTX_init(&shsInfo->ossl_sha1_ctx ); + EVP_DigestInit_ex(&shsInfo->ossl_sha1_ctx , EVP_sha1(), NULL); +} + +/* Update SHS for a block of data */ + +void shsUpdate(SHS_INFO *shsInfo, const SHS_BYTE *buffer, unsigned int count) +{ + EVP_DigestUpdate(&shsInfo->ossl_sha1_ctx , buffer, count); +} +/* Final wrapup - pad to SHS_DATASIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +void shsFinal(SHS_INFO *shsInfo) +{ + unsigned char *digest_buf = NULL; + + digest_buf = (unsigned char *)OPENSSL_malloc( sizeof(shsInfo->digest)); + + EVP_DigestFinal_ex(&shsInfo->ossl_sha1_ctx , digest_buf , &shsInfo->digest_len); + + memcpy(shsInfo->digest, digest_buf, shsInfo->digest_len); + OPENSSL_free(digest_buf); + EVP_MD_CTX_cleanup(&shsInfo->ossl_sha1_ctx ); +} diff --git a/src/lib/crypto/openssl/sha1/shs.h b/src/lib/crypto/openssl/sha1/shs.h new file mode 100644 index 0000000000..66e91b69bb --- /dev/null +++ b/src/lib/crypto/openssl/sha1/shs.h @@ -0,0 +1,49 @@ +#ifndef _SHS_DEFINED + +#include "k5-int.h" +#include +#include + +#define _SHS_DEFINED + +/* Some useful types */ + +typedef krb5_octet SHS_BYTE; +typedef krb5_ui_4 SHS_LONG; + +/* Define the following to use the updated SHS implementation */ +#define NEW_SHS /**/ + +/* The SHS block size and message digest sizes, in bytes */ + +#define SHS_DATASIZE 64 +#define SHS_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + EVP_MD_CTX ossl_sha1_ctx; + unsigned int digest_len; + SHS_LONG digest[ 5 ]; /* Message digest */ + SHS_LONG countLo, countHi; /* 64-bit bit count */ + SHS_LONG data[ 16 ]; /* SHS data buffer */ +} SHS_INFO; + +/* Message digest functions (shs.c) */ +void shsInit(SHS_INFO *shsInfo); +void shsUpdate(SHS_INFO *shsInfo, const SHS_BYTE *buffer, unsigned int count); +void shsFinal(SHS_INFO *shsInfo); + + +/* Keyed Message digest functions (hmac_sha.c) */ +krb5_error_code hmac_sha(krb5_octet *text, + int text_len, + krb5_octet *key, + int key_len, + krb5_octet *digest); + + +#define NIST_SHA_CKSUM_LENGTH SHS_DIGESTSIZE +#define HMAC_SHA_CKSUM_LENGTH SHS_DIGESTSIZE + +#endif /* _SHS_DEFINED */ diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h index f7c35acfd4..4a8db057b6 100644 --- a/src/lib/gssapi/generic/gssapi_ext.h +++ b/src/lib/gssapi/generic/gssapi_ext.h @@ -254,6 +254,36 @@ OM_uint32 KRB5_CALLCONV gss_release_iov_buffer gss_iov_buffer_desc *, /* iov */ int); /* iov_count */ +/* + * Protocol transition + */ +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const 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 */ + +OM_uint32 KRB5_CALLCONV +gss_add_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + const 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 */ + /* * Naming extensions */ diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in index 804e7803d5..b84efa1769 100644 --- a/src/lib/gssapi/krb5/Makefile.in +++ b/src/lib/gssapi/krb5/Makefile.in @@ -74,6 +74,7 @@ SRCS = \ $(srcdir)/rel_cred.c \ $(srcdir)/rel_oid.c \ $(srcdir)/rel_name.c \ + $(srcdir)/s4u_gss_glue.c \ $(srcdir)/seal.c \ $(srcdir)/set_allowable_enctypes.c \ $(srcdir)/ser_sctx.c \ @@ -125,6 +126,7 @@ OBJS = \ $(OUTPRE)rel_cred.$(OBJEXT) \ $(OUTPRE)rel_oid.$(OBJEXT) \ $(OUTPRE)rel_name.$(OBJEXT) \ + $(OUTPRE)s4u_gss_glue.$(OBJEXT) \ $(OUTPRE)seal.$(OBJEXT) \ $(OUTPRE)set_allowable_enctypes.$(OBJEXT) \ $(OUTPRE)ser_sctx.$(OBJEXT) \ @@ -179,6 +181,7 @@ STLIBOBJS = \ rel_cred.o \ rel_oid.o \ rel_name.o \ + s4u_gss_glue.o \ seal.o \ set_allowable_enctypes.o \ ser_sctx.o \ diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index f50da2ab78..934302cffd 100644 --- a/src/lib/gssapi/krb5/accept_sec_context.c +++ b/src/lib/gssapi/krb5/accept_sec_context.c @@ -114,6 +114,53 @@ #ifndef LEAN_CLIENT +static OM_uint32 +create_constrained_deleg_creds(OM_uint32 *minor_status, + krb5_gss_cred_id_t verifier_cred_handle, + krb5_ticket *ticket, + krb5_gss_cred_id_t *out_cred, + krb5_context context) +{ + OM_uint32 major_status; + krb5_creds krb_creds; + krb5_data *data; + krb5_error_code code; + + assert(out_cred != NULL); + assert(verifier_cred_handle->usage == GSS_C_BOTH); + + memset(&krb_creds, 0, sizeof(krb_creds)); + krb_creds.client = ticket->enc_part2->client; + krb_creds.server = ticket->server; + krb_creds.keyblock = *(ticket->enc_part2->session); + krb_creds.ticket_flags = ticket->enc_part2->flags; + krb_creds.times = ticket->enc_part2->times; + krb_creds.magic = KV5M_CREDS; + krb_creds.authdata = NULL; + + code = encode_krb5_ticket(ticket, &data); + if (code) { + *minor_status = code; + return GSS_S_FAILURE; + } + + krb_creds.ticket = *data; + + major_status = kg_compose_deleg_cred(minor_status, + verifier_cred_handle, + &krb_creds, + GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, + out_cred, + NULL, + NULL, + context); + + krb5_free_data(context, data); + + return major_status; +} + /* Decode, decrypt and store the forwarded creds in the local ccache. */ static krb5_error_code rd_and_store_for_creds(context, auth_context, inbuf, out_cred) @@ -873,6 +920,23 @@ kg_accept_krb5(minor_status, context_handle, ctx->krb_times = ticket->enc_part2->times; /* struct copy */ ctx->krb_flags = ticket->enc_part2->flags; + if (delegated_cred_handle != NULL && + deleg_cred == NULL && /* no unconstrained delegation */ + cred->usage == GSS_C_BOTH && + (ticket->enc_part2->flags & TKT_FLG_FORWARDABLE)) { + /* + * Now, we always fabricate a delegated credentials handle + * containing the service ticket to ourselves, which can be + * used for S4U2Proxy. + */ + major_status = create_constrained_deleg_creds(minor_status, cred, + ticket, &deleg_cred, + context); + if (GSS_ERROR(major_status)) + goto fail; + ctx->gss_flags |= GSS_C_DELEG_FLAG; + } + krb5_free_ticket(context, ticket); /* Done with ticket */ { @@ -1057,8 +1121,8 @@ kg_accept_krb5(minor_status, context_handle, if (src_name) *src_name = (gss_name_t) name; - if (delegated_cred_handle && deleg_cred) { - if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { + if (delegated_cred_handle) { + if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) { major_status = GSS_S_FAILURE; code = G_VALIDATE_FAILED; goto fail; diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c index 681f18a67d..8f8cf1e2ce 100644 --- a/src/lib/gssapi/krb5/acquire_cred.c +++ b/src/lib/gssapi/krb5/acquire_cred.c @@ -538,8 +538,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req, cred->usage = cred_usage; cred->name = NULL; - cred->prerfc_mech = req_old; - cred->rfc_mech = req_new; + cred->prerfc_mech = (req_old != 0); + cred->rfc_mech = (req_new != 0); #ifndef LEAN_CLIENT cred->keytab = NULL; @@ -766,3 +766,4 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status, *minor_status = 0; return GSS_S_COMPLETE; } + diff --git a/src/lib/gssapi/krb5/copy_ccache.c b/src/lib/gssapi/krb5/copy_ccache.c index e7b48e04f1..19fe1d788a 100644 --- a/src/lib/gssapi/krb5/copy_ccache.c +++ b/src/lib/gssapi/krb5/copy_ccache.c @@ -50,8 +50,11 @@ gss_krb5int_copy_ccache(OM_uint32 *minor_status, krb5_free_context(context); return(GSS_S_FAILURE); } - while (!code && !krb5_cc_next_cred(context, k5creds->ccache, &cursor, &creds)) + while (!code && !krb5_cc_next_cred(context, k5creds->ccache, &cursor, + &creds)) { code = krb5_cc_store_cred(context, out_ccache, &creds); + krb5_free_cred_contents(context, &creds); + } krb5_cc_end_seq_get(context, k5creds->ccache, &cursor); k5_mutex_unlock(&k5creds->lock); *minor_status = code; diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h index 2591b2a1c8..f85e03835d 100644 --- a/src/lib/gssapi/krb5/gssapiP_krb5.h +++ b/src/lib/gssapi/krb5/gssapiP_krb5.h @@ -166,8 +166,9 @@ typedef struct _krb5_gss_cred_id_rec { /* name/type of credential */ gss_cred_usage_t usage; krb5_gss_name_t name; - int prerfc_mech; - int rfc_mech; + unsigned int prerfc_mech : 1; + unsigned int rfc_mech : 1; + unsigned int proxy_cred : 1; /* keytab (accept) data */ krb5_keytab keytab; @@ -470,6 +471,29 @@ krb5_boolean kg_integ_only_iov(gss_iov_buffer_desc *iov, int iov_count); krb5_error_code kg_allocate_iov(gss_iov_buffer_t iov, size_t size); +krb5_error_code +krb5_to_gss_cred(krb5_context context, + krb5_creds *creds, + krb5_gss_cred_id_t *out_cred); + +OM_uint32 +kg_new_connection( + OM_uint32 *minor_status, + krb5_gss_cred_id_t cred, + 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, + krb5_context context, + int default_mech); + /** declarations of internal name mechanism functions **/ OM_uint32 krb5_gss_acquire_cred @@ -770,6 +794,17 @@ OM_uint32 krb5_gss_validate_cred gss_cred_id_t /* cred */ ); +OM_uint32 krb5_gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const 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 */ + OM_uint32 krb5_gss_validate_cred_1(OM_uint32 * /* minor_status */, gss_cred_id_t /* cred_handle */, @@ -876,6 +911,18 @@ krb5_gss_release_any_name_mapping(OM_uint32 *minor_status, gss_buffer_t type_id, gss_any_t *input); +/* s4u_gss_glue.c */ +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t *output_cred, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec, + krb5_context context); + /* * These take unglued krb5-mech-specific contexts. */ diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c index 445647e38d..d7acd52b78 100644 --- a/src/lib/gssapi/krb5/gssapi_krb5.c +++ b/src/lib/gssapi/krb5/gssapi_krb5.c @@ -140,6 +140,7 @@ const gss_OID_desc krb5_gss_oid_array[] = { /* gss_nt_krb5_principal. Object identifier for a krb5_principal. Do not use. */ {10, "\052\206\110\206\367\022\001\002\002\002"}, + { 0, 0 } }; @@ -447,13 +448,11 @@ krb5_gss_inquire_cred_by_oid(OM_uint32 *minor_status, /* * gss_set_sec_context_option() methods */ -#if 0 static struct { gss_OID_desc oid; OM_uint32 (*func)(OM_uint32 *, gss_ctx_id_t *, const gss_OID, const gss_buffer_t); } krb5_gss_set_sec_context_option_ops[] = { }; -#endif static OM_uint32 krb5_gss_set_sec_context_option (OM_uint32 *minor_status, @@ -481,12 +480,8 @@ krb5_gss_set_sec_context_option (OM_uint32 *minor_status, return GSS_S_NO_CONTEXT; ctx = (krb5_gss_ctx_id_rec *) context_handle; - - if (!ctx->established) - return GSS_S_NO_CONTEXT; } -#if 0 for (i = 0; i < sizeof(krb5_gss_set_sec_context_option_ops)/ sizeof(krb5_gss_set_sec_context_option_ops[0]); i++) { if (g_OID_prefix_equal(desired_object, &krb5_gss_set_sec_context_option_ops[i].oid)) { @@ -496,7 +491,6 @@ krb5_gss_set_sec_context_option (OM_uint32 *minor_status, value); } } -#endif *minor_status = EINVAL; @@ -521,7 +515,7 @@ static struct { { {GSS_KRB5_SET_CRED_RCACHE_OID_LENGTH, GSS_KRB5_SET_CRED_RCACHE_OID}, gss_krb5int_set_cred_rcache - } + }, }; static OM_uint32 @@ -587,7 +581,7 @@ static struct { { {GSS_KRB5_USE_KDC_CONTEXT_OID_LENGTH, GSS_KRB5_USE_KDC_CONTEXT_OID}, krb5int_gss_use_kdc_context - } + }, }; static OM_uint32 @@ -683,6 +677,8 @@ static struct gss_config krb5_mechanism = { krb5_gss_unwrap_iov, krb5_gss_wrap_iov_length, NULL, /* complete_auth_token */ + krb5_gss_acquire_cred_impersonate_name, + NULL, /* krb5_gss_add_cred_impersonate_name */ NULL, /* display_name_ext */ krb5_gss_inquire_name, krb5_gss_get_name_attribute, diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c index 0fb412730c..eace54f344 100644 --- a/src/lib/gssapi/krb5/init_sec_context.c +++ b/src/lib/gssapi/krb5/init_sec_context.c @@ -128,15 +128,54 @@ static krb5_error_code get_credentials(context, cred, server, now, krb5_creds **out_creds; { krb5_error_code code; - krb5_creds in_creds; + krb5_creds in_creds, evidence_creds; + krb5_flags flags = 0; + krb5_principal cc_princ = NULL; k5_mutex_assert_locked(&cred->lock); memset(&in_creds, 0, sizeof(krb5_creds)); + memset(&evidence_creds, 0, sizeof(krb5_creds)); in_creds.client = in_creds.server = NULL; assert(cred->name != NULL); - in_creds.client = cred->name->princ; + if ((code = krb5_cc_get_principal(context, cred->ccache, &cc_princ))) + goto cleanup; + + /* + * Do constrained delegation if we have proxy credentials and + * we're not trying to get a ticket to ourselves (in which case + * we can just use the S4U2Self or evidence ticket directly). + */ + if (cred->proxy_cred && + !krb5_principal_compare(context, cc_princ, server->princ)) { + krb5_creds mcreds; + + flags |= KRB5_GC_CANONICALIZE | + KRB5_GC_NO_STORE | + KRB5_GC_CONSTRAINED_DELEGATION; + + memset(&mcreds, 0, sizeof(mcreds)); + + mcreds.magic = KV5M_CREDS; + mcreds.times.endtime = cred->tgt_expire; + mcreds.server = cc_princ; + mcreds.client = cred->name->princ; + + code = krb5_cc_retrieve_cred(context, cred->ccache, + KRB5_TC_MATCH_TIMES, &mcreds, + &evidence_creds); + if (code) + goto cleanup; + + assert(evidence_creds.ticket_flags & TKT_FLG_FORWARDABLE); + + in_creds.client = cc_princ; + in_creds.second_ticket = evidence_creds.ticket; + } else { + in_creds.client = cred->name->princ; + } + in_creds.server = server->princ; in_creds.times.endtime = endtime; in_creds.authdata = NULL; @@ -155,11 +194,20 @@ static krb5_error_code get_credentials(context, cred, server, now, goto cleanup; } - code = krb5_get_credentials(context, 0, cred->ccache, + code = krb5_get_credentials(context, flags, cred->ccache, &in_creds, out_creds); if (code) goto cleanup; + if (flags & KRB5_GC_CONSTRAINED_DELEGATION) { + if (!krb5_principal_compare(context, cred->name->princ, + (*out_creds)->client)) { + /* server did not support constrained delegation */ + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } + /* * Enforce a stricter limit (without timeskew forgiveness at the * boundaries) because accept_sec_context code is also similarly @@ -173,6 +221,8 @@ static krb5_error_code get_credentials(context, cred, server, now, cleanup: krb5_free_authdata(context, in_creds.authdata); + krb5_free_principal(context, cc_princ); + krb5_free_cred_contents(context, &evidence_creds); return code; } @@ -407,8 +457,8 @@ cleanup: * * Do the grunt work of setting up a new context. */ -static OM_uint32 -new_connection( +OM_uint32 +kg_new_connection( OM_uint32 *minor_status, krb5_gss_cred_id_t cred, gss_ctx_id_t *context_handle, @@ -947,12 +997,12 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle, /*SUPPRESS 29*/ if (*context_handle == GSS_C_NO_CONTEXT) { - major_status = new_connection(minor_status, cred, context_handle, - target_name, mech_type, req_flags, - time_req, input_chan_bindings, - input_token, actual_mech_type, - output_token, ret_flags, time_rec, - context, default_mech); + major_status = kg_new_connection(minor_status, cred, context_handle, + target_name, mech_type, req_flags, + time_req, input_chan_bindings, + input_token, actual_mech_type, + output_token, ret_flags, time_rec, + context, default_mech); k5_mutex_unlock(&cred->lock); if (*context_handle == GSS_C_NO_CONTEXT) { save_error_info (*minor_status, context); diff --git a/src/lib/gssapi/krb5/krb5_gss_glue.c b/src/lib/gssapi/krb5/krb5_gss_glue.c index f9bf03016f..0345501221 100644 --- a/src/lib/gssapi/krb5/krb5_gss_glue.c +++ b/src/lib/gssapi/krb5/krb5_gss_glue.c @@ -416,3 +416,4 @@ gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, return GSS_S_COMPLETE; } + diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c new file mode 100644 index 0000000000..f91d4fb345 --- /dev/null +++ b/src/lib/gssapi/krb5/s4u_gss_glue.c @@ -0,0 +1,359 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ +#include "k5-int.h" +#include "gssapiP_krb5.h" +#ifdef HAVE_MEMORY_H +#include +#endif +#include + +static OM_uint32 +kg_set_desired_mechs(OM_uint32 *minor_status, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t cred) +{ + unsigned int i; + + if (desired_mechs == GSS_C_NULL_OID_SET) { + cred->prerfc_mech = 1; + cred->rfc_mech = 1; + } else { + cred->prerfc_mech = 0; + cred->rfc_mech = 0; + + for (i = 0; i < desired_mechs->count; i++) { + if (g_OID_equal(gss_mech_krb5_old, &desired_mechs->elements[i])) + cred->prerfc_mech = 1; + else if (g_OID_equal(gss_mech_krb5, &desired_mechs->elements[i])) + cred->rfc_mech = 1; + } + + if (!cred->prerfc_mech && !cred->rfc_mech) { + *minor_status = 0; + return GSS_S_BAD_MECH; + } + } + + return GSS_S_COMPLETE; +} + +static OM_uint32 +kg_return_mechs(OM_uint32 *minor_status, + krb5_gss_cred_id_t cred, + gss_OID_set *actual_mechs) +{ + OM_uint32 major_status, minor; + gss_OID_set mechs; + + if (actual_mechs == NULL) + return GSS_S_COMPLETE; + + major_status = generic_gss_create_empty_oid_set(minor_status, &mechs); + if (GSS_ERROR(major_status)) + return major_status; + + if (cred->prerfc_mech) { + major_status = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5_old, + &mechs); + if (GSS_ERROR(major_status)) { + generic_gss_release_oid_set(&minor, &mechs); + return major_status; + } + } + if (cred->rfc_mech) { + major_status = generic_gss_add_oid_set_member(minor_status, + gss_mech_krb5, + &mechs); + if (GSS_ERROR(major_status)) { + generic_gss_release_oid_set(&minor, &mechs); + return major_status; + } + } + + *actual_mechs = mechs; + + return GSS_S_COMPLETE; +} + +static int +kg_is_initiator_cred(krb5_gss_cred_id_t cred) +{ + return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) && + (cred->ccache != NULL); +} + +static OM_uint32 +kg_impersonate_name(OM_uint32 *minor_status, + const krb5_gss_cred_id_t impersonator_cred, + const krb5_gss_name_t user, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t *output_cred, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_creds in_creds, *out_creds = NULL; + + memset(&in_creds, 0, sizeof(in_creds)); + memset(&out_creds, 0, sizeof(out_creds)); + + in_creds.client = user->princ; + in_creds.server = impersonator_cred->name->princ; + + if (impersonator_cred->req_enctypes != NULL) + in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0]; + + if (user->ad_context != NULL) { + code = krb5_authdata_export_attributes(context, + user->ad_context, + AD_USAGE_TGS_REQ, + &in_creds.authdata); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + } + + code = krb5_get_credentials_for_user(context, + KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE, + impersonator_cred->ccache, + &in_creds, + NULL, &out_creds); + if (code != 0) { + krb5_free_authdata(context, in_creds.authdata); + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = kg_compose_deleg_cred(minor_status, + impersonator_cred, + out_creds, + time_req, + desired_mechs, + output_cred, + actual_mechs, + time_rec, + context); + + krb5_free_authdata(context, in_creds.authdata); + krb5_free_creds(context, out_creds); + + return major_status; +} + +OM_uint32 +krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const 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) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred; + krb5_context context; + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (desired_name == GSS_C_NO_NAME) + return GSS_S_CALL_INACCESSIBLE_READ; + + if (output_cred_handle == NULL) + return GSS_S_CALL_INACCESSIBLE_WRITE; + + if (cred_usage != GSS_C_INITIATE) { + *minor_status = (OM_uint32)G_BAD_USAGE; + return GSS_S_FAILURE; + } + + *output_cred_handle = GSS_C_NO_CREDENTIAL; + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + if (time_rec != NULL) + *time_rec = 0; + + code = krb5_gss_init_context(&context); + if (code != 0) { + *minor_status = code; + return GSS_S_FAILURE; + } + + major_status = krb5_gss_validate_cred_1(minor_status, + impersonator_cred_handle, + context); + if (GSS_ERROR(major_status)) { + krb5_free_context(context); + return major_status; + } + + major_status = kg_impersonate_name(minor_status, + (krb5_gss_cred_id_t)impersonator_cred_handle, + (krb5_gss_name_t)desired_name, + time_req, + desired_mechs, + &cred, + actual_mechs, + time_rec, + context); + + *output_cred_handle = (gss_cred_id_t)cred; + + k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock); + krb5_free_context(context); + + return major_status; + +} + +OM_uint32 +kg_compose_deleg_cred(OM_uint32 *minor_status, + krb5_gss_cred_id_t impersonator_cred, + krb5_creds *subject_creds, + OM_uint32 time_req, + const gss_OID_set desired_mechs, + krb5_gss_cred_id_t *output_cred, + gss_OID_set *actual_mechs, + OM_uint32 *time_rec, + krb5_context context) +{ + OM_uint32 major_status; + krb5_error_code code; + krb5_gss_cred_id_t cred = NULL; + + k5_mutex_assert_locked(&impersonator_cred->lock); + + if (!kg_is_initiator_cred(impersonator_cred) || + impersonator_cred->name == NULL || + impersonator_cred->proxy_cred) { + code = G_BAD_USAGE; + goto cleanup; + } + + assert(impersonator_cred->name->princ != NULL); + + assert(subject_creds != NULL); + assert(subject_creds->client != NULL); + + cred = xmalloc(sizeof(*cred)); + if (cred == NULL) { + code = ENOMEM; + goto cleanup; + } + memset(cred, 0, sizeof(*cred)); + + code = k5_mutex_init(&cred->lock); + if (code != 0) + goto cleanup; + + /* + * Only return a "proxy" credential for use with constrained + * delegation if the subject credentials are forwardable. + * Submitting non-forwardable credentials to the KDC for use + * with constrained delegation will only return an error. + */ + cred->usage = GSS_C_INITIATE; + cred->proxy_cred = !!(subject_creds->ticket_flags & TKT_FLG_FORWARDABLE); + + major_status = kg_set_desired_mechs(minor_status, desired_mechs, cred); + if (GSS_ERROR(major_status)) + goto cleanup; + + cred->tgt_expire = impersonator_cred->tgt_expire; + + code = kg_init_name(context, subject_creds->client, NULL, 0, &cred->name); + if (code != 0) + goto cleanup; + + code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); + if (code != 0) + goto cleanup; + + code = krb5_cc_initialize(context, cred->ccache, + cred->proxy_cred ? impersonator_cred->name->princ : + subject_creds->client); + if (code != 0) + goto cleanup; + + if (cred->proxy_cred) { + /* Impersonator's TGT will be necessary for S4U2Proxy */ + code = krb5_cc_copy_creds(context, impersonator_cred->ccache, + cred->ccache); + if (code != 0) + goto cleanup; + } + + code = krb5_cc_store_cred(context, cred->ccache, subject_creds); + if (code != 0) + goto cleanup; + + if (time_rec != NULL) { + krb5_timestamp now; + + code = krb5_timeofday(context, &now); + if (code != 0) + goto cleanup; + + *time_rec = cred->tgt_expire - now; + } + + major_status = kg_return_mechs(minor_status, cred, actual_mechs); + if (GSS_ERROR(major_status)) + goto cleanup; + + if (!kg_save_cred_id((gss_cred_id_t)cred)) { + code = G_VALIDATE_FAILED; + goto cleanup; + } + + major_status = GSS_S_COMPLETE; + *minor_status = 0; + *output_cred = cred; + +cleanup: + if (code != 0) { + *minor_status = code; + major_status = GSS_S_FAILURE; + } + + if (GSS_ERROR(major_status) && cred != NULL) { + k5_mutex_destroy(&cred->lock); + krb5_cc_destroy(context, cred->ccache); + kg_release_name(context, 0, &cred->name); + xfree(cred); + } + + return major_status; +} + diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c index 4bd1f33793..747d8222e6 100644 --- a/src/lib/gssapi/krb5/val_cred.c +++ b/src/lib/gssapi/krb5/val_cred.c @@ -58,7 +58,8 @@ krb5_gss_validate_cred_1(OM_uint32 *minor_status, gss_cred_id_t cred_handle, *minor_status = code; return(GSS_S_DEFECTIVE_CREDENTIAL); } - if (!krb5_principal_compare(context, princ, cred->name->princ)) { + if (!cred->proxy_cred && + !krb5_principal_compare(context, princ, cred->name->princ)) { k5_mutex_unlock(&cred->lock); *minor_status = KG_CCACHE_NOMATCH; return(GSS_S_DEFECTIVE_CREDENTIAL); diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports index 8d560cecf4..60754df7a7 100644 --- a/src/lib/gssapi/libgssapi_krb5.exports +++ b/src/lib/gssapi/libgssapi_krb5.exports @@ -9,8 +9,10 @@ GSS_C_NT_USER_NAME GSS_KRB5_NT_PRINCIPAL_NAME gss_accept_sec_context gss_acquire_cred +gss_acquire_cred_impersonate_name gss_add_buffer_set_member gss_add_cred +gss_add_cred_impersonate_name gss_add_oid_set_member gss_canonicalize_name gss_compare_name diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in index c645357bbb..61972ab759 100644 --- a/src/lib/gssapi/mechglue/Makefile.in +++ b/src/lib/gssapi/mechglue/Makefile.in @@ -14,6 +14,7 @@ DEFS=-D_GSS_STATIC_LINK=1 SRCS = \ $(srcdir)/g_accept_sec_context.c \ $(srcdir)/g_acquire_cred.c \ + $(srcdir)/g_acquire_cred_imp_name.c \ $(srcdir)/g_buffer_set.c \ $(srcdir)/g_canon_name.c \ $(srcdir)/g_compare_name.c \ @@ -66,6 +67,7 @@ SRCS = \ OBJS = \ $(OUTPRE)g_accept_sec_context.$(OBJEXT) \ $(OUTPRE)g_acquire_cred.$(OBJEXT) \ + $(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \ $(OUTPRE)g_buffer_set.$(OBJEXT) \ $(OUTPRE)g_canon_name.$(OBJEXT) \ $(OUTPRE)g_compare_name.$(OBJEXT) \ @@ -118,6 +120,7 @@ OBJS = \ STLIBOBJS = \ g_accept_sec_context.o \ g_acquire_cred.o \ + g_acquire_cred_imp_name.o \ g_buffer_set.o \ g_canon_name.o \ g_compare_name.o \ diff --git a/src/lib/gssapi/mechglue/g_accept_sec_context.c b/src/lib/gssapi/mechglue/g_accept_sec_context.c index fa703d34d2..dc43915935 100644 --- a/src/lib/gssapi/mechglue/g_accept_sec_context.c +++ b/src/lib/gssapi/mechglue/g_accept_sec_context.c @@ -121,6 +121,7 @@ gss_cred_id_t * d_cred; gss_name_t tmp_src_name = GSS_C_NO_NAME; gss_OID_desc token_mech_type_desc; gss_OID token_mech_type = &token_mech_type_desc; + gss_OID actual_mech = GSS_C_NO_OID; gss_mechanism mech; status = val_acc_sec_ctx_args(minor_status, @@ -198,8 +199,8 @@ gss_cred_id_t * d_cred; input_cred_handle, input_token_buffer, input_chan_bindings, - &internal_name, - mech_type, + src_name ? &internal_name : NULL, + &actual_mech, output_token, &temp_ret_flags, time_rec, @@ -222,110 +223,120 @@ gss_cred_id_t * d_cred; * then call gss_import_name() to create * the union name struct cast to src_name */ - if (internal_name != NULL) { - temp_status = gssint_convert_name_to_union_name( - &temp_minor_status, mech, - internal_name, &tmp_src_name); - if (temp_status != GSS_S_COMPLETE) { - *minor_status = temp_minor_status; - map_error(minor_status, mech); - if (output_token->length) - (void) gss_release_buffer(&temp_minor_status, - output_token); - if (internal_name != GSS_C_NO_NAME) - mech->gss_release_name( - &temp_minor_status, - &internal_name); - return (temp_status); - } - if (src_name != NULL) { + if (src_name != NULL) { + if (internal_name != GSS_C_NO_NAME) { + /* consumes internal_name regardless of success */ + temp_status = gssint_convert_name_to_union_name( + &temp_minor_status, mech, + internal_name, &tmp_src_name); + if (temp_status != GSS_S_COMPLETE) { + *minor_status = temp_minor_status; + map_error(minor_status, mech); + if (output_token->length) + (void) gss_release_buffer(&temp_minor_status, + output_token); + return (temp_status); + } *src_name = tmp_src_name; - } - } else if (src_name != NULL) { - *src_name = GSS_C_NO_NAME; + } else + *src_name = GSS_C_NO_NAME; } +#define g_OID_prefix_equal(o1, o2) \ + (((o1)->length >= (o2)->length) && \ + (memcmp((o1)->elements, (o2)->elements, (o2)->length) == 0)) + /* Ensure we're returning correct creds format */ if ((temp_ret_flags & GSS_C_DELEG_FLAG) && tmp_d_cred != GSS_C_NO_CREDENTIAL) { - gss_union_cred_t d_u_cred = NULL; - - d_u_cred = malloc(sizeof (gss_union_cred_desc)); - if (d_u_cred == NULL) { - status = GSS_S_FAILURE; - goto error_out; - } - (void) memset(d_u_cred, 0, - sizeof (gss_union_cred_desc)); - - d_u_cred->count = 1; + if (actual_mech != GSS_C_NO_OID && + !g_OID_prefix_equal(actual_mech, token_mech_type)) { + *d_cred = tmp_d_cred; /* unwrapped pseudo-mech */ + } else { + gss_union_cred_t d_u_cred = NULL; - status = generic_gss_copy_oid(&temp_minor_status, - token_mech_type, - &d_u_cred->mechs_array); + d_u_cred = malloc(sizeof (gss_union_cred_desc)); + if (d_u_cred == NULL) { + status = GSS_S_FAILURE; + goto error_out; + } + (void) memset(d_u_cred, 0, sizeof (gss_union_cred_desc)); - if (status != GSS_S_COMPLETE) { - free(d_u_cred); - goto error_out; - } + d_u_cred->count = 1; - d_u_cred->cred_array = malloc(sizeof (gss_cred_id_t)); - if (d_u_cred->cred_array != NULL) { - d_u_cred->cred_array[0] = tmp_d_cred; - } else { - free(d_u_cred); - status = GSS_S_FAILURE; - goto error_out; - } + status = generic_gss_copy_oid(&temp_minor_status, + token_mech_type, + &d_u_cred->mechs_array); - internal_name = GSS_C_NO_NAME; + if (status != GSS_S_COMPLETE) { + free(d_u_cred); + goto error_out; + } - d_u_cred->auxinfo.creation_time = time(0); - d_u_cred->auxinfo.time_rec = 0; - d_u_cred->loopback = d_u_cred; + d_u_cred->cred_array = malloc(sizeof(gss_cred_id_t)); + if (d_u_cred->cred_array != NULL) { + d_u_cred->cred_array[0] = tmp_d_cred; + } else { + free(d_u_cred); + status = GSS_S_FAILURE; + goto error_out; + } - if (mech->gss_inquire_cred) { - status = mech->gss_inquire_cred(minor_status, - tmp_d_cred, - &internal_name, - &d_u_cred->auxinfo.time_rec, - &d_u_cred->auxinfo.cred_usage, - NULL); - if (status != GSS_S_COMPLETE) - map_error(minor_status, mech); - } + d_u_cred->auxinfo.creation_time = time(0); + d_u_cred->auxinfo.time_rec = 0; + d_u_cred->loopback = d_u_cred; + + internal_name = GSS_C_NO_NAME; + + if (mech->gss_inquire_cred) { + status = mech->gss_inquire_cred(minor_status, + tmp_d_cred, + &internal_name, + &d_u_cred->auxinfo.time_rec, + &d_u_cred->auxinfo.cred_usage, + NULL); + if (status != GSS_S_COMPLETE) + map_error(minor_status, mech); + } - if (internal_name != NULL) { - temp_status = gssint_convert_name_to_union_name( - &temp_minor_status, mech, - internal_name, &tmp_src_name); - if (temp_status != GSS_S_COMPLETE) { - *minor_status = temp_minor_status; - map_error(minor_status, mech); - if (output_token->length) - (void) gss_release_buffer( + if (internal_name != GSS_C_NO_NAME) { + /* consumes internal_name regardless of success */ + temp_status = gssint_convert_name_to_union_name( + &temp_minor_status, mech, + internal_name, &tmp_src_name); + if (temp_status != GSS_S_COMPLETE) { + *minor_status = temp_minor_status; + map_error(minor_status, mech); + if (output_token->length) + (void) gss_release_buffer( + &temp_minor_status, + output_token); + (void) gss_release_oid(&temp_minor_status, + &actual_mech); + free(d_u_cred->cred_array); + free(d_u_cred); + return (temp_status); + } + + if (tmp_src_name != GSS_C_NO_NAME) { + status = gss_display_name( &temp_minor_status, - output_token); - free(d_u_cred->cred_array); - free(d_u_cred); - return (temp_status); + tmp_src_name, + &d_u_cred->auxinfo.name, + &d_u_cred->auxinfo.name_type); + (void) gss_release_name(&temp_minor_status, + &tmp_src_name); + } } - } - if (tmp_src_name != NULL) { - status = gss_display_name( - &temp_minor_status, - tmp_src_name, - &d_u_cred->auxinfo.name, - &d_u_cred->auxinfo.name_type); + *d_cred = (gss_cred_id_t)d_u_cred; } - - *d_cred = (gss_cred_id_t)d_u_cred; } - if (src_name == NULL && tmp_src_name != NULL) - (void) gss_release_name(&temp_minor_status, - &tmp_src_name); + if (mech_type != NULL) + *mech_type = actual_mech; + else + (void) gss_release_oid(&temp_minor_status, &actual_mech); if (ret_flags != NULL) *ret_flags = temp_ret_flags; return (status); diff --git a/src/lib/gssapi/mechglue/g_acquire_cred.c b/src/lib/gssapi/mechglue/g_acquire_cred.c index fada9e8872..6dfc65f7b2 100644 --- a/src/lib/gssapi/mechglue/g_acquire_cred.c +++ b/src/lib/gssapi/mechglue/g_acquire_cred.c @@ -2,7 +2,7 @@ /* * 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 @@ -12,7 +12,7 @@ * 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 @@ -35,42 +35,6 @@ #include #include -static gss_OID_set -create_actual_mechs(mechs_array, count) - const gss_OID mechs_array; - int count; -{ - gss_OID_set actual_mechs; - int i; - OM_uint32 minor; - - actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc)); - if (!actual_mechs) - return NULL; - - actual_mechs->elements = (gss_OID) - malloc(sizeof (gss_OID_desc) * count); - if (!actual_mechs->elements) { - free(actual_mechs); - return NULL; - } - - actual_mechs->count = 0; - - for (i = 0; i < count; i++) { - actual_mechs->elements[i].elements = (void *) - malloc(mechs_array[i].length); - if (actual_mechs->elements[i].elements == NULL) { - (void) gss_release_oid_set(&minor, &actual_mechs); - return (NULL); - } - g_OID_copy(&actual_mechs->elements[i], &mechs_array[i]); - actual_mechs->count++; - } - - return actual_mechs; -} - static OM_uint32 val_acq_cred_args( OM_uint32 *minor_status, @@ -172,7 +136,7 @@ OM_uint32 * time_rec; mech = gssint_get_mechanism(NULL); if (mech == NULL) return (GSS_S_BAD_MECH); - + mechs = &default_OID_set; default_OID_set.count = 1; default_OID_set.elements = &default_OID; @@ -234,12 +198,16 @@ OM_uint32 * time_rec; * setup the actual mechs output parameter */ if (actual_mechs != NULL) { - if ((*actual_mechs = create_actual_mechs(creds->mechs_array, - creds->count)) == NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { (void) gss_release_cred(minor_status, (gss_cred_id_t *)&creds); - *minor_status = 0; - return (GSS_S_FAILURE); + return (major); } } @@ -312,7 +280,7 @@ OM_uint32 KRB5_CALLCONV 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, + output_cred_handle, actual_mechs, initiator_time_rec, acceptor_time_rec) OM_uint32 *minor_status; gss_cred_id_t input_cred_handle; @@ -434,7 +402,7 @@ gss_add_cred(minor_status, input_cred_handle, status = mech->gss_display_name(&temp_minor_status, internal_name, &union_cred->auxinfo.name, &union_cred->auxinfo.name_type); - + if (status != GSS_S_COMPLETE) goto errout; } @@ -475,10 +443,14 @@ gss_add_cred(minor_status, input_cred_handle, g_OID_copy(&new_mechs_array[union_cred->count], &mech->mech_type); - if (actual_mechs) { - *actual_mechs = create_actual_mechs(new_mechs_array, - union_cred->count + 1); - if (*actual_mechs == NULL) { + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { free(new_mechs_array[union_cred->count].elements); goto errout; } diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c b/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c new file mode 100644 index 0000000000..9ba6a1faa7 --- /dev/null +++ b/src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c @@ -0,0 +1,549 @@ +/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */ + +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ +/* + * 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_acquire_cred_impersonate_name + */ + +#include "mglueP.h" +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include + +static OM_uint32 +val_acq_cred_impersonate_name_args( + OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const 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) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NULL_OID_SET; + + if (time_rec != NULL) + *time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +OM_uint32 KRB5_CALLCONV +gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + OM_uint32 time_req, + const 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) +{ + OM_uint32 major = GSS_S_FAILURE; + OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE; + gss_OID_set_desc default_OID_set; + gss_OID_set mechs; + gss_OID_desc default_OID; + gss_mechanism mech; + unsigned int i; + gss_union_cred_t creds; + + major = val_acq_cred_impersonate_name_args(minor_status, + impersonator_cred_handle, + desired_name, + time_req, + desired_mechs, + cred_usage, + output_cred_handle, + actual_mechs, + time_rec); + if (major != GSS_S_COMPLETE) + return (major); + + /* Initial value needed below. */ + major = GSS_S_FAILURE; + + /* + * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an + * appropriate default. We use the first mechanism in the + * mechansim list as the default. This set is created with + * statics thus needs not be freed + */ + if(desired_mechs == GSS_C_NULL_OID_SET) { + mech = gssint_get_mechanism(NULL); + if (mech == NULL) + return (GSS_S_BAD_MECH); + + mechs = &default_OID_set; + default_OID_set.count = 1; + default_OID_set.elements = &default_OID; + default_OID.length = mech->mech_type.length; + default_OID.elements = mech->mech_type.elements; + } else + mechs = desired_mechs; + + if (mechs->count == 0) + return (GSS_S_BAD_MECH); + + /* allocate the output credential structure */ + creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc)); + if (creds == NULL) + return (GSS_S_FAILURE); + + /* initialize to 0s */ + (void) memset(creds, 0, sizeof (gss_union_cred_desc)); + creds->loopback = creds; + + /* for each requested mech attempt to obtain a credential */ + for (i = 0; i < mechs->count; i++) { + major = gss_add_cred_impersonate_name(minor_status, + (gss_cred_id_t)creds, + impersonator_cred_handle, + desired_name, + &mechs->elements[i], + cred_usage, + time_req, + time_req, NULL, + NULL, + &initTimeOut, + &acceptTimeOut); + if (major == GSS_S_COMPLETE) { + /* update the credential's time */ + if (cred_usage == GSS_C_ACCEPT) { + if (outTime > acceptTimeOut) + outTime = acceptTimeOut; + } else if (cred_usage == GSS_C_INITIATE) { + if (outTime > initTimeOut) + outTime = initTimeOut; + } else { + /* + * time_rec is the lesser of the + * init/accept times + */ + if (initTimeOut > acceptTimeOut) + outTime = (outTime > acceptTimeOut) ? + acceptTimeOut : outTime; + else + outTime = (outTime > initTimeOut) ? + initTimeOut : outTime; + } + } + } /* for */ + + /* ensure that we have at least one credential element */ + if (creds->count < 1) { + free(creds); + return (major); + } + + /* + * fill in output parameters + * setup the actual mechs output parameter + */ + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = creds->count; + oids.elements = creds->mechs_array; + + major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(major)) { + (void) gss_release_cred(minor_status, + (gss_cred_id_t *)&creds); + return (major); + } + } + + if (time_rec) + *time_rec = outTime; + + + creds->loopback = creds; + *output_cred_handle = (gss_cred_id_t)creds; + return (GSS_S_COMPLETE); +} + +static OM_uint32 +val_add_cred_impersonate_name_args( + OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_cred_id_t impersonator_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) +{ + + /* Initialize outputs. */ + + if (minor_status != NULL) + *minor_status = 0; + + if (output_cred_handle != NULL) + *output_cred_handle = GSS_C_NO_CREDENTIAL; + + if (actual_mechs != NULL) + *actual_mechs = GSS_C_NO_OID_SET; + + if (acceptor_time_rec != NULL) + *acceptor_time_rec = 0; + + if (initiator_time_rec != NULL) + *initiator_time_rec = 0; + + /* Validate arguments. */ + + if (minor_status == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE); + + if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED); + + if (desired_name == GSS_C_NO_NAME) + return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && + output_cred_handle == NULL) + return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED); + + if (cred_usage != GSS_C_ACCEPT + && cred_usage != GSS_C_INITIATE + && cred_usage != GSS_C_BOTH) { + if (minor_status) { + *minor_status = EINVAL; + map_errcode(minor_status); + } + return GSS_S_FAILURE; + } + + return (GSS_S_COMPLETE); +} + + +/* V2 KRB5_CALLCONV */ +OM_uint32 KRB5_CALLCONV +gss_add_cred_impersonate_name(OM_uint32 *minor_status, + gss_cred_id_t input_cred_handle, + const gss_cred_id_t impersonator_cred_handle, + const gss_name_t desired_name, + const 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) +{ + OM_uint32 status, temp_minor_status; + OM_uint32 time_req, time_rec; + gss_union_name_t union_name; + gss_union_cred_t new_union_cred, union_cred; + gss_cred_id_t mech_impersonator_cred; + gss_name_t internal_name = GSS_C_NO_NAME; + gss_name_t allocated_name = GSS_C_NO_NAME; + gss_mechanism mech; + gss_cred_id_t cred = NULL; + gss_OID new_mechs_array = NULL; + gss_cred_id_t * new_cred_array = NULL; + + status = val_add_cred_impersonate_name_args(minor_status, + input_cred_handle, + impersonator_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); + if (status != GSS_S_COMPLETE) + return (status); + + mech = gssint_get_mechanism(desired_mech); + if (!mech) + return GSS_S_BAD_MECH; + else if (!mech->gss_acquire_cred) + return (GSS_S_UNAVAILABLE); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL) { + union_cred = malloc(sizeof (gss_union_cred_desc)); + if (union_cred == NULL) + return (GSS_S_FAILURE); + + (void) memset(union_cred, 0, sizeof (gss_union_cred_desc)); + + /* for default credentials we will use GSS_C_NO_NAME */ + internal_name = GSS_C_NO_NAME; + } else { + union_cred = (gss_union_cred_t)input_cred_handle; + if (gssint_get_mechanism_cred(union_cred, desired_mech) != + GSS_C_NO_CREDENTIAL) + return (GSS_S_DUPLICATE_ELEMENT); + } + + mech_impersonator_cred = + gssint_get_mechanism_cred((gss_union_cred_t)impersonator_cred_handle, + desired_mech); + if (mech_impersonator_cred == GSS_C_NO_CREDENTIAL) + return (GSS_S_NO_CRED); + + /* may need to create a mechanism specific name */ + union_name = (gss_union_name_t)desired_name; + if (union_name->mech_type && + g_OID_equal(union_name->mech_type, + &mech->mech_type)) + internal_name = union_name->mech_name; + else { + if (gssint_import_internal_name(minor_status, + &mech->mech_type, union_name, + &allocated_name) != GSS_S_COMPLETE) + return (GSS_S_BAD_NAME); + internal_name = allocated_name; + } + + if (cred_usage == GSS_C_ACCEPT) + time_req = acceptor_time_req; + else if (cred_usage == GSS_C_INITIATE) + time_req = initiator_time_req; + else if (cred_usage == GSS_C_BOTH) + time_req = (acceptor_time_req > initiator_time_req) ? + acceptor_time_req : initiator_time_req; + else + time_req = 0; + + status = mech->gss_acquire_cred_impersonate_name(minor_status, + mech_impersonator_cred, + internal_name, + time_req, + GSS_C_NULL_OID_SET, + cred_usage, + &cred, + NULL, + &time_rec); + if (status != GSS_S_COMPLETE) { + map_error(minor_status, mech); + goto errout; + } + + /* may need to set credential auxinfo strucutre */ + if (union_cred->auxinfo.creation_time == 0) { + union_cred->auxinfo.creation_time = time(NULL); + union_cred->auxinfo.time_rec = time_rec; + union_cred->auxinfo.cred_usage = cred_usage; + + /* + * we must set the name; if name is not supplied + * we must do inquire cred to get it + */ + if (internal_name == NULL) { + if (mech->gss_inquire_cred == NULL || + ((status = mech->gss_inquire_cred( + &temp_minor_status, cred, + &allocated_name, NULL, NULL, + NULL)) != GSS_S_COMPLETE)) + goto errout; + internal_name = allocated_name; + } + + if (internal_name != GSS_C_NO_NAME) { + status = mech->gss_display_name(&temp_minor_status, internal_name, + &union_cred->auxinfo.name, + &union_cred->auxinfo.name_type); + + if (status != GSS_S_COMPLETE) + goto errout; + } + } + + /* now add the new credential elements */ + new_mechs_array = (gss_OID) + malloc(sizeof (gss_OID_desc) * (union_cred->count+1)); + + new_cred_array = (gss_cred_id_t *) + malloc(sizeof (gss_cred_id_t) * (union_cred->count+1)); + + if (!new_mechs_array || !new_cred_array) { + status = GSS_S_FAILURE; + goto errout; + } + + if (acceptor_time_rec) + if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) + *acceptor_time_rec = time_rec; + if (initiator_time_rec) + if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) + *initiator_time_rec = time_rec; + + /* + * OK, expand the mechanism array and the credential array + */ + (void) memcpy(new_mechs_array, union_cred->mechs_array, + sizeof (gss_OID_desc) * union_cred->count); + (void) memcpy(new_cred_array, union_cred->cred_array, + sizeof (gss_cred_id_t) * union_cred->count); + + new_cred_array[union_cred->count] = cred; + if ((new_mechs_array[union_cred->count].elements = + malloc(mech->mech_type.length)) == NULL) + goto errout; + + g_OID_copy(&new_mechs_array[union_cred->count], + &mech->mech_type); + + if (actual_mechs != NULL) { + gss_OID_set_desc oids; + + oids.count = union_cred->count + 1; + oids.elements = new_mechs_array; + + status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs); + if (GSS_ERROR(status)) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + } + + if (output_cred_handle == NULL) { + free(union_cred->mechs_array); + free(union_cred->cred_array); + new_union_cred = union_cred; + } else { + new_union_cred = malloc(sizeof (gss_union_cred_desc)); + if (new_union_cred == NULL) { + free(new_mechs_array[union_cred->count].elements); + goto errout; + } + *new_union_cred = *union_cred; + *output_cred_handle = (gss_cred_id_t)new_union_cred; + } + + new_union_cred->mechs_array = new_mechs_array; + new_union_cred->cred_array = new_cred_array; + new_union_cred->count++; + new_union_cred->loopback = new_union_cred; + + /* We're done with the internal name. Free it if we allocated it. */ + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + return (GSS_S_COMPLETE); + +errout: + if (new_mechs_array) + free(new_mechs_array); + if (new_cred_array) + free(new_cred_array); + + if (cred != NULL && mech->gss_release_cred) + mech->gss_release_cred(&temp_minor_status, &cred); + + if (allocated_name) + (void) gssint_release_internal_name(&temp_minor_status, + &mech->mech_type, + &allocated_name); + + if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) { + if (union_cred->auxinfo.name.value) + free(union_cred->auxinfo.name.value); + free(union_cred); + } + + return (status); +} diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c index aafc20e06e..711c58fd8a 100644 --- a/src/lib/gssapi/mechglue/g_glue.c +++ b/src/lib/gssapi/mechglue/g_glue.c @@ -660,25 +660,9 @@ gssint_get_mechanism_cred(union_cred, mech_type) if (union_cred == GSS_C_NO_CREDENTIAL) return GSS_C_NO_CREDENTIAL; - /* SPNEGO mechanism will again call into GSSAPI */ - if (g_OID_equal(&gss_spnego_mechanism_oid_desc, mech_type)) - return (gss_cred_id_t)union_cred; - for (i=0; i < union_cred->count; i++) { if (g_OID_equal(mech_type, &union_cred->mechs_array[i])) return union_cred->cred_array[i]; - - /* for SPNEGO, check the next-lower set of creds */ - if (g_OID_equal(&gss_spnego_mechanism_oid_desc, &union_cred->mechs_array[i])) { - gss_union_cred_t candidate_cred; - gss_cred_id_t sub_cred; - - candidate_cred = (gss_union_cred_t)union_cred->cred_array[i]; - sub_cred = gssint_get_mechanism_cred(candidate_cred, mech_type); - - if(sub_cred != GSS_C_NO_CREDENTIAL) - return sub_cred; - } } return GSS_C_NO_CREDENTIAL; } diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c index b944131647..41aa6821bb 100644 --- a/src/lib/gssapi/mechglue/g_initialize.c +++ b/src/lib/gssapi/mechglue/g_initialize.c @@ -761,7 +761,10 @@ build_dynamicMech(void *dl, const gss_OID mech_type) GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_unwrap_iov); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_wrap_iov_length); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_complete_auth_token); - /* Naming extensions */ + /* Services4User (introduced in 1.8) */ + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_impersonate_name); + GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_add_cred_impersonate_name); + /* Naming extensions (introduced in 1.8) */ GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_display_name_ext); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_inquire_name); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_get_name_attribute); diff --git a/src/lib/gssapi/mechglue/g_set_context_option.c b/src/lib/gssapi/mechglue/g_set_context_option.c index 68625242be..91e4eb4531 100644 --- a/src/lib/gssapi/mechglue/g_set_context_option.c +++ b/src/lib/gssapi/mechglue/g_set_context_option.c @@ -71,8 +71,8 @@ gss_set_sec_context_option (OM_uint32 *minor_status, return GSS_S_UNAVAILABLE; status = mech->gss_set_sec_context_option(minor_status, - ctx ? &internal_ctx : - &ctx->internal_ctx_id, + ctx ? &ctx->internal_ctx_id : + &internal_ctx, desired_object, value); if (status == GSS_S_COMPLETE) { diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h index b60cfb196a..93e9a2b071 100644 --- a/src/lib/gssapi/mechglue/mglueP.h +++ b/src/lib/gssapi/mechglue/mglueP.h @@ -473,6 +473,37 @@ typedef struct gss_config { gss_buffer_t /* input_message_buffer */ ); + /* New for 1.8 */ + + OM_uint32 (*gss_acquire_cred_impersonate_name) + ( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const 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 */ + /* */); + + OM_uint32 (*gss_add_cred_impersonate_name) + ( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* input_cred_handle */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + const 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 */ + /* */); + OM_uint32 (*gss_display_name_ext) ( OM_uint32 *, /* minor_status */ @@ -543,6 +574,7 @@ typedef struct gss_config { gss_buffer_t, /* type_id */ gss_any_t * /* input */ /* */); + } *gss_mechanism; /* This structure MUST NOT be used by any code outside libgss */ diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h index 181f44b077..7793383ca1 100644 --- a/src/lib/gssapi/spnego/gssapiP_spnego.h +++ b/src/lib/gssapi/spnego/gssapiP_spnego.h @@ -218,6 +218,16 @@ OM_uint32 spnego_gss_release_name gss_name_t * /* input_name */ ); +OM_uint32 spnego_gss_inquire_cred +( + OM_uint32 *, /* minor_status */ + gss_cred_id_t, /* cred_handle */ + gss_name_t *, /* name */ + OM_uint32 *, /* lifetime */ + int *, /* cred_usage */ + gss_OID_set * /* mechanisms */ +); + OM_uint32 spnego_gss_inquire_names_for_mech ( OM_uint32 *, /* minor_status */ @@ -332,6 +342,15 @@ spnego_gss_inquire_sec_context_by_oid gss_buffer_set_t *data_set ); +OM_uint32 +spnego_gss_inquire_cred_by_oid +( + OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set +); + OM_uint32 spnego_gss_set_sec_context_option ( @@ -411,6 +430,18 @@ spnego_gss_complete_auth_token gss_buffer_t input_message_buffer ); +OM_uint32 +spnego_gss_acquire_cred_impersonate_name( + OM_uint32 *, /* minor_status */ + const gss_cred_id_t, /* impersonator_cred_handle */ + const gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + const 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 */ + OM_uint32 spnego_gss_display_name_ext ( diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c index 178223e430..740ae8e9d8 100644 --- a/src/lib/gssapi/spnego/spnego_mech.c +++ b/src/lib/gssapi/spnego/spnego_mech.c @@ -231,7 +231,7 @@ static struct gss_config spnego_mechanism = spnego_gss_display_name, spnego_gss_import_name, spnego_gss_release_name, - NULL, /* gss_inquire_cred */ + spnego_gss_inquire_cred, /* gss_inquire_cred */ NULL, /* gss_add_cred */ #ifndef LEAN_CLIENT spnego_gss_export_sec_context, /* gss_export_sec_context */ @@ -248,7 +248,7 @@ static struct gss_config spnego_mechanism = NULL, /* gss_export_name */ NULL, /* gss_store_cred */ spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */ - NULL, /* gss_inquire_cred_by_oid */ + spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */ spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */ NULL, /* gssspi_set_cred_option */ NULL, /* gssspi_mech_invoke */ @@ -258,6 +258,9 @@ static struct gss_config spnego_mechanism = spnego_gss_unwrap_iov, spnego_gss_wrap_iov_length, spnego_gss_complete_auth_token, + spnego_gss_complete_auth_token, + spnego_gss_acquire_cred_impersonate_name, + NULL, /* gss_add_cred_impersonate_name */ spnego_gss_display_name_ext, spnego_gss_inquire_name, spnego_gss_get_name_attribute, @@ -1689,6 +1692,13 @@ cleanup: *src_name = sc->internal_name; } release_spnego_ctx(&sc); + } else if (ret != GSS_S_CONTINUE_NEEDED) { + if (sc != NULL) { + gss_delete_sec_context(&tmpmin, &sc->ctx_handle, + GSS_C_NO_BUFFER); + release_spnego_ctx(&sc); + } + *context_handle = GSS_C_NO_CONTEXT; } gss_release_buffer(&tmpmin, &mechtok_out); if (mechtok_in != GSS_C_NO_BUFFER) { @@ -1788,6 +1798,76 @@ spnego_gss_release_name( return (status); } +OM_uint32 +spnego_gss_inquire_cred( + OM_uint32 *minor_status, + gss_cred_id_t cred_handle, + gss_name_t *name, + OM_uint32 *lifetime, + int *cred_usage, + gss_OID_set *mechanisms) +{ + OM_uint32 status; + gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; + OM_uint32 tmp_minor_status; + OM_uint32 initiator_lifetime, acceptor_lifetime; + + dsyslog("Entering inquire_cred\n"); + + /* + * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is + * supplied we call gss_inquire_cred_by_mech() on the + * first non-SPNEGO mechanism. + */ + if (cred_handle == GSS_C_NO_CREDENTIAL) { + status = get_available_mechs(minor_status, + GSS_C_NO_NAME, + GSS_C_BOTH, + &creds, + mechanisms); + if (status != GSS_S_COMPLETE) { + dsyslog("Leaving inquire_cred\n"); + return (status); + } + + if ((*mechanisms)->count == 0) { + gss_release_cred(&tmp_minor_status, &creds); + gss_release_oid_set(&tmp_minor_status, mechanisms); + dsyslog("Leaving inquire_cred\n"); + return (GSS_S_DEFECTIVE_CREDENTIAL); + } + + assert((*mechanisms)->elements != NULL); + + status = gss_inquire_cred_by_mech(minor_status, + creds, + &(*mechanisms)->elements[0], + name, + &initiator_lifetime, + &acceptor_lifetime, + cred_usage); + if (status != GSS_S_COMPLETE) { + gss_release_cred(&tmp_minor_status, &creds); + dsyslog("Leaving inquire_cred\n"); + return (status); + } + + if (lifetime != NULL) + *lifetime = (*cred_usage == GSS_C_ACCEPT) ? + acceptor_lifetime : initiator_lifetime; + + gss_release_cred(&tmp_minor_status, &creds); + } else { + status = gss_inquire_cred(minor_status, cred_handle, + name, lifetime, + cred_usage, mechanisms); + } + + dsyslog("Leaving inquire_cred\n"); + + return (status); +} + /*ARGSUSED*/ OM_uint32 spnego_gss_compare_name( @@ -2091,6 +2171,21 @@ spnego_gss_inquire_sec_context_by_oid( return (ret); } +OM_uint32 +spnego_gss_inquire_cred_by_oid( + OM_uint32 *minor_status, + const gss_cred_id_t cred_handle, + const gss_OID desired_object, + gss_buffer_set_t *data_set) +{ + OM_uint32 ret; + ret = gss_inquire_cred_by_oid(minor_status, + cred_handle, + desired_object, + data_set); + return (ret); +} + OM_uint32 spnego_gss_set_sec_context_option( OM_uint32 *minor_status, @@ -2221,6 +2316,53 @@ spnego_gss_complete_auth_token( return (ret); } +OM_uint32 +spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, + const gss_cred_id_t impersonator_cred_handle, + 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) +{ + OM_uint32 status; + gss_OID_set amechs = GSS_C_NULL_OID_SET; + + dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n"); + + if (actual_mechs) + *actual_mechs = NULL; + + if (time_rec) + *time_rec = 0; + + if (desired_mechs == GSS_C_NO_OID_SET) { + status = gss_inquire_cred(minor_status, + impersonator_cred_handle, + NULL, NULL, + NULL, &amechs); + if (status != GSS_S_COMPLETE) + return status; + + desired_mechs = amechs; + } + + status = gss_acquire_cred_impersonate_name(minor_status, + impersonator_cred_handle, + desired_name, time_req, + desired_mechs, cred_usage, + output_cred_handle, actual_mechs, + time_rec); + + if (amechs != GSS_C_NULL_OID_SET) + (void) gss_release_oid_set(minor_status, &amechs); + + dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n"); + return (status); +} + OM_uint32 spnego_gss_display_name_ext(OM_uint32 *minor_status, gss_name_t name, diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c index 2bd99adbc1..51637f7de4 100644 --- a/src/lib/kadm5/str_conv.c +++ b/src/lib/kadm5/str_conv.c @@ -78,6 +78,8 @@ 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_ok_to_auth_as_delegate_in[] = "ok-to-auth-as-delegate"; +static const char flags_no_auth_data_required_in[] = "no-auth-data-required"; 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"; @@ -85,13 +87,15 @@ 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_preauth_out[] = "Preauthentication required"; +static const char flags_hwauth_out[] = "HW authentication required"; static const char flags_ok_as_delegate_out[] = "OK as Delegate"; 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_ok_to_auth_as_delegate_out[] = "Protocol transition with delegation allowed"; +static const char flags_no_auth_data_required_out[] = "No authorization data required"; static const char flags_default_neg[] = "-"; static const char flags_default_sep[] = " "; @@ -115,7 +119,9 @@ static const struct flags_lookup_entry flags_table[] = { { 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 } +{ KRB5_KDB_SUPPORT_DESMD5, 1, flags_md5_in, flags_md5_out }, +{ KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, 1, flags_ok_to_auth_as_delegate_in, flags_ok_to_auth_as_delegate_out }, +{ KRB5_KDB_NO_AUTH_DATA_REQUIRED, 1, flags_no_auth_data_required_in, flags_no_auth_data_required_out } }; static const int flags_table_nents = sizeof(flags_table)/ sizeof(flags_table[0]); diff --git a/src/lib/kadm5/unit-test/config/unix.exp b/src/lib/kadm5/unit-test/config/unix.exp index f14f1263ba..0bbd72dad5 100644 --- a/src/lib/kadm5/unit-test/config/unix.exp +++ b/src/lib/kadm5/unit-test/config/unix.exp @@ -131,85 +131,85 @@ proc api_start {} { set pid [spawn $API] expect { -re "$prompt$" {} - eof { error "EOF starting API" } - timeout { error "Timeout starting API" } + eof { perror "EOF starting API" } + timeout { perror "Timeout starting API" } } if {! [info exists env(TCLUTIL)]} { - error "TCLUTIL environment variable isn't set" + perror "TCLUTIL environment variable isn't set" } # tcl 8.4 for some reason screws up autodetection of output # EOL translation. Work around it for now. send "if { \[info commands fconfigure\] ne \"\" } { fconfigure stdout -translation lf }\n" expect { -re "$prompt$" {} - eof { error "EOF starting API" } - timeout { error "Timeout starting API" } + eof { perror "EOF starting API" } + timeout { perror "Timeout starting API" } } send "source $env(TCLUTIL)\n" expect { -re "$prompt$" {} - eof { error "EOF starting API" } - timeout { error "Timeout starting API" } + eof { perror "EOF starting API" } + timeout { perror "Timeout starting API" } } send "set current_struct_version \[expr \$KADM5_STRUCT_VERSION &~ \$KADM5_STRUCT_VERSION_MASK\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } send "set current_api_version \[expr \$KADM5_API_VERSION_2 &~ \$KADM5_API_VERSION_MASK\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "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"} + eof { perror "EOF setting API varibles"} + timeout { perror "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"} + eof { perror "EOF setting API varibles"} + timeout { perror "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"} + eof { perror "EOF setting API varibles"} + timeout { perror "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"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } send "set old_api_version \[expr \$KADM5_API_VERSION_MASK | 0x00\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } send "set old_struct_version \[expr \$KADM5_STRUCT_VERSION_MASK | 0x00\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } send "set new_api_version \[expr \$KADM5_API_VERSION_MASK | 0xca\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } send "set new_struct_version \[expr \$KADM5_STRUCT_VERSION_MASK | 0xca\]\n" expect { -re "$prompt$" {} - eof { error "EOF setting API varibles"} - timeout { error "timeout setting API varibles"} + eof { perror "EOF setting API varibles"} + timeout { perror "timeout setting API varibles"} } set api_pid $pid diff --git a/src/lib/kadm5/unit-test/lib/lib.t b/src/lib/kadm5/unit-test/lib/lib.t index 361c727da8..9537fc36aa 100644 --- a/src/lib/kadm5/unit-test/lib/lib.t +++ b/src/lib/kadm5/unit-test/lib/lib.t @@ -22,7 +22,7 @@ proc lib_start_api {} { $KADM5_STRUCT_VERSION $KADM5_API_VERSION_2 \ lib_handle }]} { - error "$test: unexpected failure in init" + perror "$test: unexpected failure in init" return } verbose "+++ restarted api ($lib_pid) for lib" @@ -40,7 +40,7 @@ proc cmd {command} { expect { -re "OK .*$prompt$" { return 1 } -re "ERROR .*$prompt$" { return 0 } - "wrong # args" { error "$test: wrong number args"; return 0 } + "wrong # args" { perror "$test: wrong number args"; return 0 } timeout { fail "$test: timeout"; return 0 } eof { fail "$test: eof"; api_exit; lib_start_api; return 0 } } @@ -52,7 +52,7 @@ proc tcl_cmd {command} { send "[string trim $command]\n" expect { -re "$prompt$" { return 1} - "wrong # args" { error "$test: wrong number args"; return 0 } + "wrong # args" { perror "$test: wrong number args"; return 0 } timeout { error_and_restart "timeout" } eof { api_exit; lib_start_api; return 0 } } @@ -69,7 +69,7 @@ proc one_line_succeed_test {command} { -re "ERROR .*$prompt$" { fail "$test: $expect_out(buffer)"; return 0 } - "wrong # args" { error "$test: wrong number args"; return 0 } + "wrong # args" { perror "$test: wrong number args"; return 0 } timeout { fail "$test: timeout"; return 0 } eof { fail "$test: eof"; api_exit; lib_start_api; return 0 } } @@ -85,7 +85,7 @@ proc one_line_fail_test {command code} { -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 } + "wrong # args" { perror "$test: wrong number args"; return 0 } timeout { fail "$test: timeout"; return 0 } eof { fail "$test: eof"; api_exit; lib_start_api; return 0 } } @@ -100,7 +100,7 @@ proc one_line_fail_test_nochk {command} { 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 } + "wrong # args" { perror "$test: wrong number args"; return 0 } timeout { fail "$test: timeout"; return 0 } eof { fail "$test: eof"; api_exit; lib_start_api; return 0 } } @@ -111,7 +111,7 @@ proc resync {} { expect { -re "$prompt$" {} - "wrong # args" { error "$test: wrong number args"; return 0 } + "wrong # args" { perror "$test: wrong number args"; return 0 } eof { api_exit; lib_start_api } } } @@ -173,7 +173,8 @@ proc principal_exists {name} { lib_start_api set ret [cmd [format { - kadm5_get_principal $lib_handle "%s" principal + kadm5_get_principal $lib_handle "%s" principal \ + KADM5_PRINCIPAL_NORMAL_MASK } $name]] # puts stdout "Finishing principal_exists." @@ -246,7 +247,7 @@ proc kinit { princ pass {opts ""} } { # the parent, which is us, to read pending data. expect { - "when initializing cache" { error "kinit failed: $expect_out(buffer)" } + "when initializing cache" { perror "kinit failed: $expect_out(buffer)" } eof {} } wait @@ -282,20 +283,20 @@ proc create_principal_with_keysalts {name keysalts} { spawn $kadmin_local -e "$keysalts" expect { "kadmin.local:" {} - default { error "waiting for kadmin.local prompt"; return 1} + default { perror "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"; + perror "expecting principal created message"; return 1 } - default { error "waiting for principal created message"; return 1 } + default { perror "waiting for principal created message"; return 1 } } expect { "kadmin.local:" {} - default { error "waiting for kadmin.local prompt"; return 1 } + default { perror "waiting for kadmin.local prompt"; return 1 } } close wait diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c index a232ffcf65..91dfd90d22 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.c +++ b/src/lib/krb5/asn.1/asn1_k_decode.c @@ -1616,6 +1616,47 @@ error_out: return retval; } +asn1_error_code asn1_decode_s4u_userid(asn1buf *buf, krb5_s4u_userid *val) +{ + setup(); + val->nonce = 0; + val->user = NULL; + val->subject_cert.data = NULL; + val->options = 0; + { begin_structure(); + get_field(val->nonce,0,asn1_decode_int32); + alloc_principal(val->user); + opt_field(val->user,1,asn1_decode_principal_name,0); + get_field(val->user,2,asn1_decode_realm); + opt_lenfield(val->subject_cert.length,val->subject_cert.data,3,asn1_decode_charstring); + opt_field(val->options,4,asn1_decode_krb5_flags,0); + end_structure(); + } + return 0; +error_out: + krb5_free_principal(NULL, val->user); + krb5_free_data_contents(NULL, &val->subject_cert); + val->user = NULL; + val->subject_cert.data = NULL; + return retval; +} + +asn1_error_code asn1_decode_pa_s4u_x509_user(asn1buf *buf, krb5_pa_s4u_x509_user *val) +{ + setup(); + val->cksum.contents = NULL; + { begin_structure(); + get_field(val->user_id,0,asn1_decode_s4u_userid); + get_field(val->cksum,1,asn1_decode_checksum); + end_structure(); + } + return 0; +error_out: + krb5_free_s4u_userid_contents(NULL, &val->user_id); + krb5_free_checksum_contents(NULL, &val->cksum); + return retval; +} + asn1_error_code asn1_decode_pa_pac_req(asn1buf *buf, krb5_pa_pac_req *val) { setup(); diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h index 0532ad4677..f0d99dcc0d 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.h +++ b/src/lib/krb5/asn.1/asn1_k_decode.h @@ -263,6 +263,10 @@ asn1_error_code asn1_decode_setpw_req (asn1buf *buf, krb5_data *rep, krb5_principal *principal); asn1_error_code asn1_decode_pa_for_user (asn1buf *buf, krb5_pa_for_user *val); +asn1_error_code asn1_decode_s4u_userid + (asn1buf *buf, krb5_s4u_userid *val); +asn1_error_code asn1_decode_pa_s4u_x509_user + (asn1buf *buf, krb5_pa_s4u_x509_user *val); asn1_error_code asn1_decode_pa_pac_req (asn1buf *buf, krb5_pa_pac_req *val); diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c index f53ac7cd7d..1e9f11fe8c 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.c +++ b/src/lib/krb5/asn.1/asn1_k_encode.c @@ -263,6 +263,8 @@ static unsigned int optional_enc_kdc_rep_part(const void *p) optional |= (1u << 8); if (val->caddrs != NULL && val->caddrs[0] != NULL) optional |= (1u << 11); + if (val->enc_padata != NULL) + optional |= (1u << 12); return optional; } @@ -1147,6 +1149,36 @@ static const struct field_info pa_for_user_fields[] = { DEFSEQTYPE(pa_for_user, krb5_pa_for_user, pa_for_user_fields, 0); +/* [MS-SFU] Section 2.2.2. */ +static const struct field_info s4u_userid_fields[] = { + FIELDOF_NORM(krb5_s4u_userid, int32, nonce, 0), + FIELDOF_OPT(krb5_s4u_userid, principal, user, 1, 1), + FIELDOF_NORM(krb5_s4u_userid, realm_of_principal, user, 2), + FIELDOF_OPT(krb5_s4u_userid, ostring_data, subject_cert, 3, 3), + FIELDOF_OPT(krb5_s4u_userid, krb5_flags, options, 4, 4), +}; + +static unsigned int s4u_userid_optional (const void *p) { + const krb5_s4u_userid *val = p; + unsigned int optional = 0; + if (val->user != NULL && val->user->length != 0) + optional |= (1u)<<1; + if (val->subject_cert.length != 0) + optional |= (1u)<<3; + if (val->options != 0) + optional |= (1u)<<4; + return optional; +} + +DEFSEQTYPE(s4u_userid, krb5_s4u_userid, s4u_userid_fields, s4u_userid_optional); + +static const struct field_info pa_s4u_x509_user_fields[] = { + FIELDOF_NORM(krb5_pa_s4u_x509_user, s4u_userid, user_id, 0), + FIELDOF_NORM(krb5_pa_s4u_x509_user, checksum, cksum, 1), +}; + +DEFSEQTYPE(pa_s4u_x509_user, krb5_pa_s4u_x509_user, pa_s4u_x509_user_fields, 0); + /* draft-ietf-krb-wg-kerberos-referrals Appendix A. */ static const struct field_info pa_svr_referral_data_fields[] = { FIELDOF_NORM(krb5_pa_svr_referral_data, realm_of_principal, principal, 0), @@ -1340,6 +1372,8 @@ MAKE_FULL_ENCODER(encode_krb5_predicted_sam_response, predicted_sam_response); MAKE_FULL_ENCODER(encode_krb5_setpw_req, setpw_req); MAKE_FULL_ENCODER(encode_krb5_pa_for_user, pa_for_user); +MAKE_FULL_ENCODER(encode_krb5_s4u_userid, s4u_userid); +MAKE_FULL_ENCODER(encode_krb5_pa_s4u_x509_user, pa_s4u_x509_user); MAKE_FULL_ENCODER(encode_krb5_pa_svr_referral_data, pa_svr_referral_data); MAKE_FULL_ENCODER(encode_krb5_pa_server_referral_data, pa_server_referral_data); MAKE_FULL_ENCODER(encode_krb5_etype_list, etype_list); diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c index e5df1428c3..215608d33a 100644 --- a/src/lib/krb5/asn.1/krb5_decode.c +++ b/src/lib/krb5/asn.1/krb5_decode.c @@ -1060,6 +1060,18 @@ decode_krb5_pa_for_user(const krb5_data *code, krb5_pa_for_user **repptr) cleanup(free); } +krb5_error_code +decode_krb5_pa_s4u_x509_user(const krb5_data *code, krb5_pa_s4u_x509_user **repptr) +{ + setup_buf_only(krb5_pa_s4u_x509_user *); + alloc_field(rep); + + retval = asn1_decode_pa_s4u_x509_user(&buf, rep); + if (retval) clean_return(retval); + + cleanup(free); +} + krb5_error_code decode_krb5_pa_pac_req(const krb5_data *code, krb5_pa_pac_req **repptr) { diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in index ca4edd893f..44cce52f0e 100644 --- a/src/lib/krb5/krb/Makefile.in +++ b/src/lib/krb5/krb/Makefile.in @@ -80,6 +80,7 @@ STLIBOBJS= \ rd_req_dec.o \ rd_safe.o \ recvauth.o \ + s4u_creds.o \ sendauth.o \ send_tgs.o \ ser_actx.o \ @@ -169,6 +170,7 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \ $(OUTPRE)rd_req_dec.$(OBJEXT) \ $(OUTPRE)rd_safe.$(OBJEXT) \ $(OUTPRE)recvauth.$(OBJEXT) \ + $(OUTPRE)s4u_creds.$(OBJEXT) \ $(OUTPRE)sendauth.$(OBJEXT) \ $(OUTPRE)send_tgs.$(OBJEXT) \ $(OUTPRE)ser_actx.$(OBJEXT) \ @@ -259,6 +261,7 @@ SRCS= $(srcdir)/addr_comp.c \ $(srcdir)/rd_req_dec.c \ $(srcdir)/rd_safe.c \ $(srcdir)/recvauth.c \ + $(srcdir)/s4u_creds.c \ $(srcdir)/sendauth.c \ $(srcdir)/send_tgs.c \ $(srcdir)/ser_actx.c \ diff --git a/src/lib/krb5/krb/gc_frm_kdc.c b/src/lib/krb5/krb/gc_frm_kdc.c index 6e4a8b4cc1..4102dd728d 100644 --- a/src/lib/krb5/krb/gc_frm_kdc.c +++ b/src/lib/krb5/krb/gc_frm_kdc.c @@ -1018,6 +1018,11 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, DUMP_PRINC("gc_from_kdc: server as requested", supplied_server); + if (in_cred->second_ticket.length != 0 && + (kdcopt & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) { + kdcopt |= KDC_OPT_ENC_TKT_IN_SKEY; + } + /* * Try requesting a service ticket from our local KDC with referrals * turned on. If the first referral succeeds, follow a referral-only @@ -1039,9 +1044,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | - kdcopt | - (in_cred->second_ticket.length ? - KDC_OPT_ENC_TKT_IN_SKEY : 0), + kdcopt, tgtptr->addresses, in_cred, out_cred); if (retval) { DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n", @@ -1059,9 +1062,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, "retrying without option.\n", referral_count + 1)); retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | - kdcopt | - (in_cred->second_ticket.length ? - KDC_OPT_ENC_TKT_IN_SKEY : 0), + kdcopt, tgtptr->addresses, in_cred, out_cred); /* Whether or not that succeeded, we're done. */ @@ -1101,9 +1102,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, retval = krb5_get_cred_via_tkt(context, tgtptr, KDC_OPT_CANONICALIZE | FLAGS2OPTS(tgtptr->ticket_flags) | - kdcopt | - (in_cred->second_ticket.length ? - KDC_OPT_ENC_TKT_IN_SKEY : 0), + kdcopt, tgtptr->addresses, in_cred, out_cred); goto cleanup; @@ -1278,9 +1277,7 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, context->use_conf_ktypes = old_use_conf_ktypes; retval = krb5_get_cred_via_tkt(context, tgtptr, FLAGS2OPTS(tgtptr->ticket_flags) | - kdcopt | - (in_cred->second_ticket.length ? - KDC_OPT_ENC_TKT_IN_SKEY : 0), + kdcopt, tgtptr->addresses, in_cred, out_cred); cleanup: diff --git a/src/lib/krb5/krb/gc_via_tkt.c b/src/lib/krb5/krb/gc_via_tkt.c index 83c8026fcd..273655ab5d 100644 --- a/src/lib/krb5/krb/gc_via_tkt.c +++ b/src/lib/krb5/krb/gc_via_tkt.c @@ -1,7 +1,7 @@ /* * lib/krb5/krb/gc_via_tgt.c * - * Copyright 1990,1991,2007,2008 by the Massachusetts Institute of Technology. + * Copyright 1990,1991,2007-2009 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -158,6 +158,27 @@ krb5_error_code krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, krb5_flags kdcoptions, krb5_address *const *address, krb5_creds *in_cred, krb5_creds **out_cred) +{ + return krb5_get_cred_via_tkt_ext (context, tkt, + kdcoptions, address, + NULL, in_cred, NULL, NULL, + NULL, NULL, out_cred, NULL); +} + +krb5_error_code +krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, + krb5_flags kdcoptions, krb5_address *const *address, + krb5_pa_data **in_padata, + krb5_creds *in_cred, + krb5_error_code (*pacb_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *pacb_data, + krb5_pa_data ***out_padata, + krb5_pa_data ***out_enc_padata, + krb5_creds **out_cred, + krb5_keyblock **out_subkey) { krb5_error_code retval; krb5_kdc_rep *dec_rep; @@ -165,6 +186,7 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, krb5_response tgsrep; krb5_enctype *enctypes = 0; krb5_keyblock *subkey = NULL; + krb5_boolean s4u2self = FALSE, second_tkt; #ifdef DEBUG_REFERRALS printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off"); @@ -179,10 +201,13 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, if (!tkt->ticket.length) return KRB5_NO_TKT_SUPPLIED; - if ((kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) && - (!in_cred->second_ticket.length)) + second_tkt = ((kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) != 0); + + if (second_tkt && !in_cred->second_ticket.length) return(KRB5_NO_2ND_TKT); + s4u2self = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_S4U_X509_USER) || + krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FOR_USER); /* check if we have the right TGT */ /* tkt->server must be equal to */ @@ -210,13 +235,12 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, enctypes[0] = in_cred->keyblock.enctype; enctypes[1] = 0; } - + retval = krb5int_send_tgs(context, kdcoptions, &in_cred->times, enctypes, in_cred->server, address, in_cred->authdata, - 0, /* no padata */ - (kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) ? - &in_cred->second_ticket : NULL, - tkt, &tgsrep, &subkey); + in_padata, + second_tkt ? &in_cred->second_ticket : NULL, + tkt, pacb_fct, pacb_data, &tgsrep, &subkey); if (enctypes) free(enctypes); if (retval) { @@ -318,8 +342,17 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, /* make sure the response hasn't been tampered with..... */ retval = 0; - if (!krb5_principal_compare(context, dec_rep->client, tkt->client)) - retval = KRB5_KDCREP_MODIFIED; + if (s4u2self && !IS_TGS_PRINC(context, dec_rep->ticket->server)) { + /* Final hop, check whether KDC supports S4U2Self */ + if (krb5_principal_compare(context, dec_rep->client, in_cred->server)) + retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) { + /* XXX for constrained delegation this check must be performed by caller + * as we don't have access to the key to decrypt the evidence ticket. + */ + if (!krb5_principal_compare(context, dec_rep->client, tkt->client)) + retval = KRB5_KDCREP_MODIFIED; + } if (retval == 0) retval = check_reply_server(context, kdcoptions, in_cred, dec_rep); @@ -356,13 +389,26 @@ krb5_get_cred_via_tkt (krb5_context context, krb5_creds *tkt, retval = KRB5_KDCREP_SKEW; goto error_3; } + + if (out_padata != NULL) { + *out_padata = dec_rep->padata; + dec_rep->padata = NULL; + } + if (out_enc_padata != NULL) { + *out_enc_padata = dec_rep->enc_part2->enc_padata; + dec_rep->enc_part2->enc_padata = NULL; + } retval = krb5_kdcrep2creds(context, dec_rep, address, &in_cred->second_ticket, out_cred); error_3:; - if (subkey != NULL) - krb5_free_keyblock(context, subkey); + if (subkey != NULL) { + if (retval == 0 && out_subkey != NULL) + *out_subkey = subkey; + else + krb5_free_keyblock(context, subkey); + } memset(dec_rep->enc_part2->session->contents, 0, dec_rep->enc_part2->session->length); diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c index c02ddedc6b..dad3e1a917 100644 --- a/src/lib/krb5/krb/get_creds.c +++ b/src/lib/krb5/krb/get_creds.c @@ -46,7 +46,7 @@ #include "k5-int.h" #include "int-proto.h" -static krb5_error_code +krb5_error_code krb5_get_credentials_core(krb5_context context, krb5_flags options, krb5_creds *in_creds, krb5_creds *mcreds, krb5_flags *fields) @@ -87,11 +87,14 @@ krb5_get_credentials_core(krb5_context context, krb5_flags options, if (ret) return ret; } - if (options & KRB5_GC_USER_USER) { + if (options & (KRB5_GC_USER_USER | KRB5_GC_CONSTRAINED_DELEGATION)) { /* also match on identical 2nd tkt and tkt encrypted in a session key */ - *fields |= KRB5_TC_MATCH_2ND_TKT|KRB5_TC_MATCH_IS_SKEY; - mcreds->is_skey = TRUE; + *fields |= KRB5_TC_MATCH_2ND_TKT; + if (options & KRB5_GC_USER_USER) { + *fields |= KRB5_TC_MATCH_IS_SKEY; + mcreds->is_skey = TRUE; + } mcreds->second_ticket = in_creds->second_ticket; if (!in_creds->second_ticket.length) return KRB5_NO_2ND_TKT; @@ -113,25 +116,35 @@ krb5_get_credentials(krb5_context context, krb5_flags options, int not_ktype; int kdcopt = 0; - retval = krb5_get_credentials_core(context, options, - in_creds, - &mcreds, &fields); + if ((options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) { + retval = krb5_get_credentials_core(context, options, + in_creds, + &mcreds, &fields); - if (retval) return retval; + if (retval) + return retval; - if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) - return ENOMEM; + if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL) + return ENOMEM; - memset(ncreds, 0, sizeof(krb5_creds)); - ncreds->magic = KV5M_CREDS; + memset(ncreds, 0, sizeof(krb5_creds)); + ncreds->magic = KV5M_CREDS; - /* The caller is now responsible for cleaning up in_creds */ - if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, - ncreds))) { - free(ncreds); - ncreds = in_creds; + /* The caller is now responsible for cleaning up in_creds */ + if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, + ncreds))) { + free(ncreds); + ncreds = in_creds; + } else { + *out_creds = ncreds; + } } else { - *out_creds = ncreds; + /* + * To do this usefully for constrained delegation, we would + * need to look inside second_ticket, which we can't do. + */ + ncreds = in_creds; + retval = KRB5_CC_NOTFOUND; } if ((retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) @@ -145,6 +158,15 @@ krb5_get_credentials(krb5_context context, krb5_flags options, if (options & KRB5_GC_CANONICALIZE) kdcopt |= KDC_OPT_CANONICALIZE; + if (options & KRB5_GC_FORWARDABLE) + kdcopt |= KDC_OPT_FORWARDABLE; + if (options & KRB5_GC_NO_TRANSIT_CHECK) + kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK; + if (options & KRB5_GC_CONSTRAINED_DELEGATION) { + if (options & KRB5_GC_USER_USER) + return EINVAL; + kdcopt |= KDC_OPT_FORWARDABLE | KDC_OPT_CNAME_IN_ADDL_TKT; + } retval = krb5_get_cred_from_kdc_opt(context, ccache, ncreds, out_creds, &tgts, kdcopt); @@ -160,6 +182,13 @@ krb5_get_credentials(krb5_context context, krb5_flags options, } krb5_free_tgt_creds(context, tgts); } + if (!retval && (options & KRB5_GC_CONSTRAINED_DELEGATION)) { + if (((*out_creds)->ticket_flags & TKT_FLG_FORWARDABLE) == 0) { + retval = KRB5_TKT_NOT_FORWARDABLE; + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + } + } /* * Translate KRB5_CC_NOTFOUND if we previously got * KRB5_CC_NOT_KTYPE from krb5_cc_retrieve_cred(), in order to @@ -175,7 +204,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options, && not_ktype) retval = KRB5_CC_NOT_KTYPE; - if (!retval) { + if (!retval && (options & KRB5_GC_NO_STORE) == 0) { /* the purpose of the krb5_get_credentials call is to * obtain a set of credentials for the caller. the * krb5_cc_store_cred() call is to optimize performance @@ -184,6 +213,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options, */ krb5_cc_store_cred(context, ccache, *out_creds); } + return retval; } @@ -337,3 +367,4 @@ krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal c return(krb5_validate_or_renew_creds(context, creds, client, ccache, in_tkt_service, 0)); } + diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c index 018676dbee..63594ddfd5 100644 --- a/src/lib/krb5/krb/get_in_tkt.c +++ b/src/lib/krb5/krb/get_in_tkt.c @@ -493,6 +493,55 @@ static const krb5_enctype get_in_tkt_enctypes[] = { 0 }; +static krb5_error_code +rewrite_server_realm(krb5_context context, + krb5_const_principal old_server, + const krb5_data *realm, + krb5_boolean tgs, + krb5_principal *server) +{ + krb5_error_code retval; + + assert(*server == NULL); + + retval = krb5_copy_principal(context, old_server, server); + if (retval) + return retval; + + krb5_free_data_contents(context, &(*server)->realm); + (*server)->realm.data = NULL; + + retval = krb5int_copy_data_contents(context, realm, &(*server)->realm); + if (retval) + goto cleanup; + + if (tgs) { + krb5_free_data_contents(context, &(*server)->data[1]); + (*server)->data[1].data = NULL; + + retval = krb5int_copy_data_contents(context, realm, &(*server)->data[1]); + if (retval) + goto cleanup; + } + +cleanup: + if (retval) { + krb5_free_principal(context, *server); + *server = NULL; + } + + return retval; +} + +static inline int +tgt_is_local_realm(krb5_creds *tgt) +{ + return (tgt->server->length == 2 + && data_eq_string(tgt->server->data[0], KRB5_TGS_NAME) + && data_eq(tgt->server->data[1], tgt->client->realm) + && data_eq(tgt->server->realm, tgt->client->realm)); +} + krb5_error_code KRB5_CALLCONV krb5_get_in_tkt(krb5_context context, krb5_flags options, @@ -521,6 +570,8 @@ krb5_get_in_tkt(krb5_context context, int use_master = 0; int referral_count = 0; krb5_principal_data referred_client; + krb5_principal referred_server = NULL; + krb5_boolean is_tgt_req; #if APPLE_PKINIT inTktDebug("krb5_get_in_tkt top\n"); @@ -616,6 +667,8 @@ krb5_get_in_tkt(krb5_context context, goto cleanup; } + is_tgt_req = tgt_is_local_realm(creds); + while (1) { if (loopcount++ > MAX_IN_TKT_LOOPS) { retval = KRB5_GET_IN_TKT_LOOP; @@ -687,6 +740,21 @@ krb5_get_in_tkt(krb5_context context, if (retval) goto cleanup; request.client = &referred_client; + + if (referred_server != NULL) { + krb5_free_principal(context, referred_server); + referred_server = NULL; + } + + retval = rewrite_server_realm(context, + creds->server, + &referred_client.realm, + is_tgt_req, + &referred_server); + if (retval) + goto cleanup; + request.server = referred_server; + continue; } else { retval = (krb5_error_code) err_reply->error @@ -739,6 +807,8 @@ cleanup: } if (referred_client.realm.data) krb5_free_data_contents(context, &referred_client.realm); + if (referred_server) + krb5_free_principal(context, referred_server); return (retval); } @@ -939,6 +1009,52 @@ sort_krb5_padata_sequence(krb5_context context, krb5_data *realm, return 0; } +static krb5_error_code +build_in_tkt_name(krb5_context context, + char *in_tkt_service, + krb5_const_principal client, + krb5_principal *server) +{ + krb5_error_code ret; + + *server = NULL; + + if (in_tkt_service) { + /* this is ugly, because so are the data structures involved. I'm + in the library, so I'm going to manipulate the data structures + directly, otherwise, it will be worse. */ + + if ((ret = krb5_parse_name(context, in_tkt_service, server))) + return ret; + + /* stuff the client realm into the server principal. + realloc if necessary */ + if ((*server)->realm.length < client->realm.length) { + char *p = realloc((*server)->realm.data, + client->realm.length); + if (p == NULL) { + krb5_free_principal(context, *server); + *server = NULL; + return ENOMEM; + } + (*server)->realm.data = p; + } + + (*server)->realm.length = client->realm.length; + memcpy((*server)->realm.data, client->realm.data, client->realm.length); + } else { + ret = krb5_build_principal_ext(context, server, + client->realm.length, + client->realm.data, + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME, + client->realm.length, + client->realm.data, + 0); + } + return ret; +} + krb5_error_code KRB5_CALLCONV krb5_get_init_creds(krb5_context context, krb5_creds *creds, @@ -1125,41 +1241,9 @@ krb5_get_init_creds(krb5_context context, client->type == KRB5_NT_ENTERPRISE_PRINCIPAL; /* service */ - - if (in_tkt_service) { - /* this is ugly, because so are the data structures involved. I'm - in the library, so I'm going to manipulate the data structures - directly, otherwise, it will be worse. */ - - if ((ret = krb5_parse_name(context, in_tkt_service, &request.server))) - goto cleanup; - - /* stuff the client realm into the server principal. - realloc if necessary */ - if (request.server->realm.length < request.client->realm.length) { - char *p = realloc(request.server->realm.data, - request.client->realm.length); - if (p == NULL) { - ret = ENOMEM; - goto cleanup; - } - request.server->realm.data = p; - } - - request.server->realm.length = request.client->realm.length; - memcpy(request.server->realm.data, request.client->realm.data, - request.client->realm.length); - } else { - if ((ret = krb5_build_principal_ext(context, &request.server, - request.client->realm.length, - request.client->realm.data, - KRB5_TGS_NAME_SIZE, - KRB5_TGS_NAME, - request.client->realm.length, - request.client->realm.data, - 0))) - goto cleanup; - } + if ((ret = build_in_tkt_name(context, in_tkt_service, + request.client, &request.server))) + goto cleanup; krb5_preauth_request_context_init(context); @@ -1337,8 +1421,10 @@ krb5_get_init_creds(krb5_context context, } preauth_to_use = out_padata; out_padata = NULL; - krb5_free_error(context, err_reply); - err_reply = NULL; + if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED) { + krb5_free_error(context, err_reply); + err_reply = NULL; + } ret = sort_krb5_padata_sequence(context, &request.server->realm, preauth_to_use); @@ -1365,6 +1451,14 @@ krb5_get_init_creds(krb5_context context, if (ret) goto cleanup; request.client = &referred_client; + + krb5_free_principal(context, request.server); + request.server = NULL; + + ret = build_in_tkt_name(context, in_tkt_service, + request.client, &request.server); + if (ret) + goto cleanup; } else { if (retry) { /* continue to next iteration */ diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h index b81fe2566b..cc0c9f2de0 100644 --- a/src/lib/krb5/krb/int-proto.h +++ b/src/lib/krb5/krb/int-proto.h @@ -59,11 +59,31 @@ krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts, int kdcopt); +krb5_error_code +krb5_get_credentials_core(krb5_context context, krb5_flags options, + krb5_creds *in_creds, krb5_creds *mcreds, + krb5_flags *fields); + #define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew) #define IS_TGS_PRINC(c, p) \ (krb5_princ_size((c), (p)) == 2 && \ data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME)) +krb5_error_code +krb5_get_cred_via_tkt_ext (krb5_context context, krb5_creds *tkt, + krb5_flags kdcoptions, krb5_address *const *address, + krb5_pa_data **in_padata, + krb5_creds *in_cred, + krb5_error_code (*gcvt_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *gcvt_data, + krb5_pa_data ***out_padata, + krb5_pa_data ***enc_padata, + krb5_creds **out_cred, + krb5_keyblock **out_subkey); + #endif /* KRB5_INT_FUNC_PROTO__ */ diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c index 49dec8b3f3..0995b30221 100644 --- a/src/lib/krb5/krb/kfree.c +++ b/src/lib/krb5/krb/kfree.c @@ -170,7 +170,7 @@ krb5_free_checksum_contents(krb5_context context, register krb5_checksum *val) if (val == NULL) return; free(val->contents); - val->contents = 0; + val->contents = NULL; } void KRB5_CALLCONV @@ -297,6 +297,7 @@ krb5_free_enc_kdc_rep_part(krb5_context context, register krb5_enc_kdc_rep_part krb5_free_last_req(context, val->last_req); krb5_free_principal(context, val->server); krb5_free_addresses(context, val->caddrs); + krb5_free_pa_data(context, val->enc_padata); free(val); } @@ -755,6 +756,30 @@ krb5_free_pa_for_user(krb5_context context, krb5_pa_for_user *req) free(req); } +void KRB5_CALLCONV +krb5_free_s4u_userid_contents(krb5_context context, krb5_s4u_userid *user_id) +{ + if (user_id == NULL) + return; + user_id->nonce = 0; + krb5_free_principal(context, user_id->user); + user_id->user = NULL; + krb5_free_data_contents(context, &user_id->subject_cert); + user_id->subject_cert.length = 0; + user_id->subject_cert.data = NULL; + user_id->options = 0; +} + +void KRB5_CALLCONV +krb5_free_pa_s4u_x509_user(krb5_context context, krb5_pa_s4u_x509_user *req) +{ + if (req == NULL) + return; + krb5_free_s4u_userid_contents(context, &req->user_id); + krb5_free_checksum_contents(context, &req->cksum); + free(req); +} + void KRB5_CALLCONV krb5_free_pa_server_referral_data(krb5_context context, krb5_pa_server_referral_data *ref) diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index e6f4215d5c..996cbfd364 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -1706,6 +1706,59 @@ krb5_error_code pa_sam_2(krb5_context context, return(0); } +static krb5_error_code pa_s4u_x509_user( + krb5_context context, + krb5_kdc_req *request, + krb5_pa_data *in_padata, + krb5_pa_data **out_padata, + krb5_data *salt, + krb5_data *s2kparams, + krb5_enctype *etype, + krb5_keyblock *as_key, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_gic_get_as_key_fct gak_fct, + void *gak_data) +{ + krb5_s4u_userid *userid = (krb5_s4u_userid *)gak_data; /* XXX private contract */ + krb5_pa_data *s4u_padata; + krb5_error_code code; + krb5_principal client; + + *out_padata = NULL; + + if (userid == NULL) + return EINVAL; + + code = krb5_copy_principal(context, request->client, &client); + if (code != 0) + return code; + + if (userid->user != NULL) + krb5_free_principal(context, userid->user); + userid->user = client; + + if (userid->subject_cert.length != 0) { + s4u_padata = malloc(sizeof(*s4u_padata)); + if (s4u_padata == NULL) + return ENOMEM; + + s4u_padata->magic = KV5M_PA_DATA; + s4u_padata->pa_type = KRB5_PADATA_S4U_X509_USER; + s4u_padata->contents = malloc(userid->subject_cert.length); + if (s4u_padata->contents == NULL) { + free(s4u_padata); + return ENOMEM; + } + memcpy(s4u_padata->contents, userid->subject_cert.data, userid->subject_cert.length); + s4u_padata->length = userid->subject_cert.length; + + *out_padata = s4u_padata; + } + + return 0; +} + /* FIXME - order significant? */ static const pa_types_t pa_types[] = { { @@ -1750,6 +1803,11 @@ static const pa_types_t pa_types[] = { pa_fx_cookie, PA_INFO, }, + { + KRB5_PADATA_S4U_X509_USER, + pa_s4u_x509_user, + PA_INFO, + }, { -1, NULL, diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c new file mode 100644 index 0000000000..613bbef1f7 --- /dev/null +++ b/src/lib/krb5/krb/s4u_creds.c @@ -0,0 +1,829 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * lib/krb5/krb/s4u_creds.c + * + * Copyright (C) 2009 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * + */ + +#include "k5-int.h" +#include "int-proto.h" + +/* Convert ticket flags to necessary KDC options */ +#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK) + +/* + * Implements S4U2Self, by which a service can request a ticket to + * itself on behalf of an arbitrary principal. + */ + +static krb5_error_code +krb5_get_as_key_noop( + krb5_context context, + krb5_principal client, + krb5_enctype etype, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_data *salt, + krb5_data *params, + krb5_keyblock *as_key, + void *gak_data) +{ + /* force a hard error, we don't actually have the key */ + return KDC_ERR_PREAUTH_FAILED; +} + +static krb5_error_code +s4u_identify_user(krb5_context context, + krb5_creds *in_creds, + krb5_data *subject_cert, + krb5_principal *canon_user) +{ + krb5_error_code code; + krb5_preauthtype ptypes[1] = { KRB5_PADATA_S4U_X509_USER }; + krb5_creds creds; + int use_master = 0; + krb5_get_init_creds_opt *opts = NULL; + krb5_gic_opt_ext *opte = NULL; + krb5_principal_data client_data; + krb5_principal client; + krb5_s4u_userid userid; + + *canon_user = NULL; + + if (in_creds->client == NULL && subject_cert == NULL) { + return EINVAL; + } + + if (in_creds->client != NULL && + krb5_princ_type(context, in_creds->client) != + KRB5_NT_ENTERPRISE_PRINCIPAL) + /* we already know the realm of the user */ + return krb5_copy_principal(context, in_creds->client, canon_user); + + memset(&creds, 0, sizeof(creds)); + + memset(&userid, 0, sizeof(userid)); + if (subject_cert != NULL) + userid.subject_cert = *subject_cert; + + code = krb5_get_init_creds_opt_alloc(context, &opts); + if (code != 0) + goto cleanup; + krb5_get_init_creds_opt_set_tkt_life(opts, 15); + krb5_get_init_creds_opt_set_renew_life(opts, 0); + krb5_get_init_creds_opt_set_forwardable(opts, 0); + krb5_get_init_creds_opt_set_proxiable(opts, 0); + krb5_get_init_creds_opt_set_canonicalize(opts, 1); + krb5_get_init_creds_opt_set_preauth_list(opts, ptypes, 1); + code = krb5int_gic_opt_to_opte(context, opts, &opte, + 0, "s4u_identify_user"); + if (code != 0) + goto cleanup; + + if (in_creds->client != NULL) + client = in_creds->client; + else { + client_data.magic = KV5M_PRINCIPAL; + client_data.realm = in_creds->server->realm; + /* should this be NULL, empty or a fixed string? XXX */ + client_data.data = NULL; + client_data.length = 0; + client_data.type = KRB5_NT_ENTERPRISE_PRINCIPAL; + client = &client_data; + } + + code = krb5_get_init_creds(context, &creds, in_creds->client, + NULL, NULL, 0, NULL, opte, + krb5_get_as_key_noop, &userid, + &use_master, NULL); + if (code == 0 || + code == KDC_ERR_PREAUTH_REQUIRED || + code == KDC_ERR_PREAUTH_FAILED) { + *canon_user = userid.user; + userid.user = NULL; + code = 0; + } + +cleanup: + krb5_free_cred_contents(context, &creds); + if (opts != NULL) + krb5_get_init_creds_opt_free(context, opts); + if (userid.user != NULL) + krb5_free_principal(context, userid.user); + + return code; +} + +static krb5_error_code +make_pa_for_user_checksum(krb5_context context, + krb5_keyblock *key, + krb5_pa_for_user *req, + krb5_checksum *cksum) +{ + krb5_error_code code; + int i; + krb5_int32 name_type; + char *p; + krb5_data data; + krb5_cksumtype cksumtype; + + data.length = 4; + for (i = 0; i < krb5_princ_size(context, req->user); i++) { + data.length += krb5_princ_component(context, req->user, i)->length; + } + data.length += krb5_princ_realm(context, req->user)->length; + data.length += req->auth_package.length; + + p = data.data = malloc(data.length); + if (data.data == NULL) + return ENOMEM; + + name_type = krb5_princ_type(context, req->user); + p[0] = (name_type >> 0 ) & 0xFF; + p[1] = (name_type >> 8 ) & 0xFF; + p[2] = (name_type >> 16) & 0xFF; + p[3] = (name_type >> 24) & 0xFF; + p += 4; + + for (i = 0; i < krb5_princ_size(context, req->user); i++) { + memcpy(p, krb5_princ_component(context, req->user, i)->data, + krb5_princ_component(context, req->user, i)->length); + p += krb5_princ_component(context, req->user, i)->length; + } + + memcpy(p, krb5_princ_realm(context, req->user)->data, + krb5_princ_realm(context, req->user)->length); + p += krb5_princ_realm(context, req->user)->length; + + memcpy(p, req->auth_package.data, req->auth_package.length); + + code = krb5int_c_mandatory_cksumtype(context, key->enctype, &cksumtype); + if (code != 0) { + free(data.data); + return code; + } + + code = krb5_c_make_checksum(context, cksumtype, key, + KRB5_KEYUSAGE_APP_DATA_CKSUM, &data, + cksum); + + free(data.data); + + return code; +} + +static krb5_error_code +build_pa_for_user(krb5_context context, + krb5_creds *tgt, + krb5_s4u_userid *userid, + krb5_pa_data **out_padata) +{ + krb5_error_code code; + krb5_pa_data *padata; + krb5_pa_for_user for_user; + krb5_data *for_user_data = NULL; + char package[] = "Kerberos"; + + if (userid->user == NULL) { + code = EINVAL; + goto cleanup; + } + + memset(&for_user, 0, sizeof(for_user)); + for_user.user = userid->user; + for_user.auth_package.data = package; + for_user.auth_package.length = sizeof(package) - 1; + + code = make_pa_for_user_checksum(context, &tgt->keyblock, + &for_user, &for_user.cksum); + if (code != 0) + goto cleanup; + + code = encode_krb5_pa_for_user(&for_user, &for_user_data); + if (code != 0) + goto cleanup; + + padata = malloc(sizeof(*padata)); + if (padata == NULL) { + code = ENOMEM; + goto cleanup; + } + + padata->magic = KV5M_PA_DATA; + padata->pa_type = KRB5_PADATA_FOR_USER; + padata->length = for_user_data->length; + padata->contents = (krb5_octet *)for_user_data->data; + + free(for_user_data); + for_user_data = NULL; + + *out_padata = padata; + +cleanup: + if (for_user.cksum.contents != NULL) + krb5_free_checksum_contents(context, &for_user.cksum); + krb5_free_data(context, for_user_data); + + return code; +} + +/* + * This function is invoked by krb5int_send_tgs() just before + * the request is encoded; it gives us access to the nonce and + * subkey without requiring them to be generated by the caller. + */ +static krb5_error_code +build_pa_s4u_x509_user(krb5_context context, + krb5_keyblock *subkey, + krb5_kdc_req *tgsreq, + void *gcvt_data) +{ + krb5_error_code code; + krb5_pa_s4u_x509_user *s4u_user = (krb5_pa_s4u_x509_user *)gcvt_data; + krb5_data *data = NULL; + krb5_pa_data **padata; + krb5_cksumtype cksumtype; + int i; + + assert(s4u_user->cksum.contents == NULL); + + s4u_user->user_id.nonce = tgsreq->nonce; + + code = encode_krb5_s4u_userid(&s4u_user->user_id, &data); + if (code != 0) + goto cleanup; + + /* [MS-SFU] 2.2.2: unusual to say the least, but enc_padata secures it */ + if (subkey->enctype == ENCTYPE_ARCFOUR_HMAC || + subkey->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + cksumtype = CKSUMTYPE_RSA_MD4; + } else { + code = krb5int_c_mandatory_cksumtype(context, subkey->enctype, + &cksumtype); + } + if (code != 0) + goto cleanup; + + code = krb5_c_make_checksum(context, cksumtype, subkey, + KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST, data, + &s4u_user->cksum); + if (code != 0) + goto cleanup; + + krb5_free_data(context, data); + data = NULL; + + code = encode_krb5_pa_s4u_x509_user(s4u_user, &data); + if (code != 0) + goto cleanup; + + assert(tgsreq->padata != NULL); + + for (i = 0; tgsreq->padata[i] != NULL; i++) + ; + + padata = realloc(tgsreq->padata, + (i + 2) * sizeof(krb5_pa_data *)); + if (padata == NULL) { + code = ENOMEM; + goto cleanup; + } + tgsreq->padata = padata; + + padata[i] = malloc(sizeof(krb5_pa_data)); + if (padata[i] == NULL) { + code = ENOMEM; + goto cleanup; + } + padata[i]->magic = KV5M_PA_DATA; + padata[i]->pa_type = KRB5_PADATA_S4U_X509_USER; + padata[i]->length = data->length; + padata[i]->contents = (krb5_octet *)data->data; + + padata[i + 1] = NULL; + + free(data); + data = NULL; + +cleanup: + if (code != 0 && s4u_user->cksum.contents != NULL) { + krb5_free_checksum_contents(context, &s4u_user->cksum); + s4u_user->cksum.contents = NULL; + } + krb5_free_data(context, data); + + return code; +} + +static krb5_error_code +verify_s4u2self_reply(krb5_context context, + krb5_keyblock *subkey, + krb5_pa_s4u_x509_user *req_s4u_user, + krb5_pa_data **rep_padata, + krb5_pa_data **enc_padata) +{ + krb5_error_code code; + krb5_pa_data *rep_s4u_padata, *enc_s4u_padata; + krb5_pa_s4u_x509_user *rep_s4u_user = NULL; + krb5_data data, *datap = NULL; + krb5_keyusage usage; + krb5_boolean valid; + krb5_boolean not_newer; + + assert(req_s4u_user != NULL); + + switch (subkey->enctype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES3_CBC_SHA1: + case ENCTYPE_DES3_CBC_RAW: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP : + not_newer = TRUE; + break; + default: + not_newer = FALSE; + break; + } + + enc_s4u_padata = krb5int_find_pa_data(context, + enc_padata, + KRB5_PADATA_S4U_X509_USER); + + /* XXX this will break newer enctypes with a MIT 1.7 KDC */ + rep_s4u_padata = krb5int_find_pa_data(context, + rep_padata, + KRB5_PADATA_S4U_X509_USER); + if (rep_s4u_padata == NULL) { + if (not_newer == FALSE || enc_s4u_padata != NULL) + return KRB5_KDCREP_MODIFIED; + else + return 0; + } + + data.length = rep_s4u_padata->length; + data.data = (char *)rep_s4u_padata->contents; + + code = decode_krb5_pa_s4u_x509_user(&data, &rep_s4u_user); + if (code != 0) + goto cleanup; + + if (rep_s4u_user->user_id.nonce != req_s4u_user->user_id.nonce) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + + code = encode_krb5_s4u_userid(&rep_s4u_user->user_id, &datap); + if (code != 0) + goto cleanup; + + if (rep_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) + usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY; + else + usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST; + + code = krb5_c_verify_checksum(context, subkey, usage, datap, + &rep_s4u_user->cksum, &valid); + if (code != 0) + goto cleanup; + if (valid == FALSE) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + + /* + * KDCs that support KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE also return + * S4U enc_padata for older (pre-AES) encryption types only. + */ + if (not_newer) { + if (enc_s4u_padata == NULL) { + if (rep_s4u_user->user_id.options & + KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } else { + if (enc_s4u_padata->length != + req_s4u_user->cksum.length + rep_s4u_user->cksum.length) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + if (memcmp(enc_s4u_padata->contents, + req_s4u_user->cksum.contents, + req_s4u_user->cksum.length) || + memcmp(&enc_s4u_padata->contents[req_s4u_user->cksum.length], + rep_s4u_user->cksum.contents, + rep_s4u_user->cksum.length)) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + } + } else if (!krb5_c_is_keyed_cksum(rep_s4u_user->cksum.checksum_type)) { + code = KRB5KRB_AP_ERR_INAPP_CKSUM; + goto cleanup; + } + +cleanup: + krb5_free_pa_s4u_x509_user(context, rep_s4u_user); + krb5_free_data(context, datap); + + return code; +} + +static krb5_error_code +krb5_get_self_cred_from_kdc(krb5_context context, + krb5_flags options, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_data *subject_cert, + krb5_data *user_realm, + krb5_creds **out_creds) +{ + krb5_error_code code; + krb5_principal tgs = NULL; + krb5_creds tgtq, s4u_creds, *tgt = NULL, *tgtptr; + krb5_creds *referral_tgts[KRB5_REFERRAL_MAXHOPS]; + krb5_pa_s4u_x509_user s4u_user; + int referral_count = 0, i; + krb5_flags kdcopt; + + memset(&tgtq, 0, sizeof(tgtq)); + memset(&s4u_creds, 0, sizeof(s4u_creds)); + memset(referral_tgts, 0, sizeof(referral_tgts)); + *out_creds = NULL; + + memset(&s4u_user, 0, sizeof(s4u_user)); + + if (in_creds->client != NULL && + krb5_princ_size(context, in_creds->client)) { + if (krb5_princ_type(context, in_creds->client) == + KRB5_NT_ENTERPRISE_PRINCIPAL) + { + code = krb5_build_principal_ext(context, + &s4u_user.user_id.user, + user_realm->length, + user_realm->data, + in_creds->client->data[0].length, + in_creds->client->data[0].data, + 0); + if (code != 0) + goto cleanup; + s4u_user.user_id.user->type = KRB5_NT_ENTERPRISE_PRINCIPAL; + } else { + code = krb5_copy_principal(context, + in_creds->client, + &s4u_user.user_id.user); + if (code != 0) + goto cleanup; + } + } else { + code = krb5_build_principal_ext(context, &s4u_user.user_id.user, + user_realm->length, + user_realm->data); + if (code != 0) + goto cleanup; + s4u_user.user_id.user->type = KRB5_NT_ENTERPRISE_PRINCIPAL; + } + if (subject_cert != NULL) + s4u_user.user_id.subject_cert = *subject_cert; + s4u_user.user_id.options = KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE; + + /* First, acquire a TGT to the user's realm. */ + code = krb5_tgtname(context, user_realm, + krb5_princ_realm(context, in_creds->server), &tgs); + if (code != 0) + goto cleanup; + + tgtq.client = in_creds->server; + tgtq.server = tgs; + + code = krb5_get_credentials(context, options, ccache, &tgtq, &tgt); + if (code != 0) + goto cleanup; + + tgtptr = tgt; + + code = krb5int_copy_creds_contents(context, in_creds, &s4u_creds); + if (code != 0) + goto cleanup; + + if (s4u_creds.client != NULL) { + krb5_free_principal(context, s4u_creds.client); + s4u_creds.client = NULL; + } + + code = krb5_copy_principal(context, in_creds->server, &s4u_creds.client); + if (code != 0) + goto cleanup; + + /* Then, walk back the referral path to S4U2Self for user */ + kdcopt = 0; + if (options & KRB5_GC_CANONICALIZE) + kdcopt |= KDC_OPT_CANONICALIZE; + if (options & KRB5_GC_FORWARDABLE) + kdcopt |= KDC_OPT_FORWARDABLE; + if (options & KRB5_GC_NO_TRANSIT_CHECK) + kdcopt |= KDC_OPT_DISABLE_TRANSITED_CHECK; + + for (referral_count = 0; + referral_count < KRB5_REFERRAL_MAXHOPS; + referral_count++) + { + krb5_pa_data **in_padata = NULL; + krb5_pa_data **out_padata = NULL; + krb5_pa_data **enc_padata = NULL; + krb5_keyblock *subkey = NULL; + + if (s4u_user.user_id.user != NULL && + krb5_princ_size(context, s4u_user.user_id.user)) { + in_padata = calloc(2, sizeof(krb5_pa_data *)); + if (in_padata == NULL) { + code = ENOMEM; + goto cleanup; + } + code = build_pa_for_user(context, + tgtptr, + &s4u_user.user_id, &in_padata[0]); + if (code != 0) { + krb5_free_pa_data(context, in_padata); + goto cleanup; + } + } + + /* Rewrite server realm to match TGS realm */ + krb5_free_data_contents(context, &s4u_creds.server->realm); + + code = krb5int_copy_data_contents(context, + &tgtptr->server->data[1], + &s4u_creds.server->realm); + if (code != 0) + goto cleanup; + + code = krb5_get_cred_via_tkt_ext(context, tgtptr, + KDC_OPT_CANONICALIZE | + FLAGS2OPTS(tgtptr->ticket_flags) | + kdcopt, + tgtptr->addresses, + in_padata, &s4u_creds, + build_pa_s4u_x509_user, &s4u_user, + &out_padata, &enc_padata, + out_creds, &subkey); + if (code != 0) { + krb5_free_checksum_contents(context, &s4u_user.cksum); + krb5_free_pa_data(context, in_padata); + goto cleanup; + } + + code = verify_s4u2self_reply(context, subkey, &s4u_user, + out_padata, enc_padata); + + krb5_free_checksum_contents(context, &s4u_user.cksum); + krb5_free_pa_data(context, in_padata); + krb5_free_pa_data(context, out_padata); + krb5_free_pa_data(context, enc_padata); + krb5_free_keyblock(context, subkey); + + if (code != 0) + goto cleanup; + + if (krb5_principal_compare(context, + in_creds->server, + (*out_creds)->server)) { + code = 0; + goto cleanup; + } else if (IS_TGS_PRINC(context, (*out_creds)->server)) { + krb5_data *r1 = &tgtptr->server->data[1]; + krb5_data *r2 = &(*out_creds)->server->data[1]; + + if (data_eq(*r1, *r2)) { + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + code = KRB5_ERR_HOST_REALM_UNKNOWN; + break; + } + for (i = 0; i < referral_count; i++) { + if (krb5_principal_compare(context, + (*out_creds)->server, + referral_tgts[i]->server)) { + code = KRB5_KDC_UNREACH; + goto cleanup; + } + } + + tgtptr = *out_creds; + referral_tgts[referral_count] = *out_creds; + *out_creds = NULL; + } else { + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + code = KRB5KRB_AP_WRONG_PRINC; /* XXX */ + break; + } + } + +cleanup: + for (i = 0; i < KRB5_REFERRAL_MAXHOPS; i++) { + if (referral_tgts[i] != NULL) + krb5_free_creds(context, referral_tgts[i]); + } + krb5_free_principal(context, tgs); + krb5_free_creds(context, tgt); + krb5_free_cred_contents(context, &s4u_creds); + krb5_free_principal(context, s4u_user.user_id.user); + krb5_free_checksum_contents(context, &s4u_user.cksum); + + return code; +} + +krb5_error_code KRB5_CALLCONV +krb5_get_credentials_for_user(krb5_context context, krb5_flags options, + krb5_ccache ccache, krb5_creds *in_creds, + krb5_data *subject_cert, + krb5_creds **out_creds) +{ + krb5_error_code code; + krb5_principal realm = NULL; + + *out_creds = NULL; + + if (options & KRB5_GC_CONSTRAINED_DELEGATION) { + code = EINVAL; + goto cleanup; + } + + if (in_creds->client != NULL) { + /* Uncanonicalised check */ + code = krb5_get_credentials(context, options | KRB5_GC_CACHED, + ccache, in_creds, out_creds); + if (code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) + goto cleanup; + + if ((options & KRB5_GC_CACHED) && !(options & KRB5_GC_CANONICALIZE)) + goto cleanup; + } + + code = s4u_identify_user(context, in_creds, subject_cert, &realm); + if (code != 0) + goto cleanup; + + code = krb5_get_credentials(context, options | KRB5_GC_CACHED, + ccache, in_creds, out_creds); + if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) + || options & KRB5_GC_CACHED) + goto cleanup; + + code = krb5_get_self_cred_from_kdc(context, options, ccache, + in_creds, subject_cert, + krb5_princ_realm(context, realm), + out_creds); + if (code != 0) + goto cleanup; + + assert(*out_creds != NULL); + + if ((options & KRB5_GC_NO_STORE) == 0) { + code = krb5_cc_store_cred(context, ccache, *out_creds); + if (code != 0) + goto cleanup; + } + +cleanup: + if (code != 0 && *out_creds != NULL) { + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + } + + krb5_free_principal(context, realm); + + return code; +} + +/* + * Exported API for constrained delegation (S4U2Proxy). + * + * This is preferable to using krb5_get_credentials directly because + * it can perform some additional checks. + */ +krb5_error_code KRB5_CALLCONV +krb5_get_credentials_for_proxy(krb5_context context, + krb5_flags options, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_ticket *evidence_tkt, + krb5_creds **out_creds) +{ + krb5_error_code code; + krb5_creds mcreds; + krb5_creds *ncreds = NULL; + krb5_flags fields; + krb5_data *evidence_tkt_data = NULL; + krb5_creds s4u_creds; + + *out_creds = NULL; + + if (in_creds == NULL || in_creds->client == NULL || + evidence_tkt == NULL || evidence_tkt->enc_part2 == NULL) { + code = EINVAL; + goto cleanup; + } + + /* + * Caller should have set in_creds->client to match evidence + * ticket client + */ + if (!krb5_principal_compare(context, evidence_tkt->enc_part2->client, + in_creds->client)) { + code = EINVAL; + goto cleanup; + } + + if ((evidence_tkt->enc_part2->flags & TKT_FLG_FORWARDABLE) == 0) { + code = KRB5_TKT_NOT_FORWARDABLE; + goto cleanup; + } + + code = krb5_get_credentials_core(context, options, in_creds, + &mcreds, &fields); + if (code != 0) + goto cleanup; + + ncreds = calloc(1, sizeof(*ncreds)); + if (ncreds == NULL) { + code = ENOMEM; + goto cleanup; + } + ncreds->magic = KV5M_CRED; + + code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds); + if (code != 0) { + free(ncreds); + ncreds = in_creds; + } else { + *out_creds = ncreds; + } + + if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) + || options & KRB5_GC_CACHED) + goto cleanup; + + code = encode_krb5_ticket(evidence_tkt, &evidence_tkt_data); + if (code != 0) + goto cleanup; + + s4u_creds = *in_creds; + s4u_creds.client = evidence_tkt->server; + s4u_creds.second_ticket = *evidence_tkt_data; + + code = krb5_get_credentials(context, + options | KRB5_GC_CONSTRAINED_DELEGATION, + ccache, + &s4u_creds, + out_creds); + if (code != 0) + goto cleanup; + + /* + * Check client name because we couldn't compare that inside + * krb5_get_credentials() (enc_part2 is unavailable in clear) + */ + if (!krb5_principal_compare(context, + evidence_tkt->enc_part2->client, + (*out_creds)->client)) { + code = KRB5_KDCREP_MODIFIED; + goto cleanup; + } + +cleanup: + if (*out_creds != NULL && code != 0) { + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + } + if (evidence_tkt_data != NULL) + krb5_free_data(context, evidence_tkt_data); + + return code; +} diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c index 97cd02bf75..eee47ed570 100644 --- a/src/lib/krb5/krb/send_tgs.c +++ b/src/lib/krb5/krb/send_tgs.c @@ -77,7 +77,7 @@ tgs_construct_tgsreq(krb5_context context, krb5_data *in_data, if (retval) goto cleanup; } - + /* Generate checksum */ if ((retval = krb5_c_make_checksum(context, cksumtype, &in_cred->keyblock, @@ -142,6 +142,9 @@ cleanup: } /* * Note that this function fills in part of rep even on failure. + * + * The pacb_fct callback allows the caller access to the nonce + * and request subkey, for binding preauthentication data */ krb5_error_code krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, @@ -149,7 +152,13 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, krb5_const_principal sname, krb5_address *const *addrs, krb5_authdata *const *authorization_data, krb5_pa_data *const *padata, const krb5_data *second_ticket, - krb5_creds *in_cred, krb5_response *rep, krb5_keyblock **subkey) + krb5_creds *in_cred, + krb5_error_code (*pacb_fct)(krb5_context, + krb5_keyblock *, + krb5_kdc_req *, + void *), + void *pacb_data, + krb5_response *rep, krb5_keyblock **subkey) { krb5_error_code retval; krb5_kdc_req tgsreq; @@ -157,13 +166,14 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, krb5_ticket *sec_ticket = 0; krb5_ticket *sec_ticket_arr[2]; krb5_timestamp time_now; - krb5_pa_data **combined_padata; + krb5_pa_data **combined_padata = NULL; krb5_pa_data ap_req_padata; int tcp_only = 0, use_master; krb5_keyblock *local_subkey = NULL; assert (subkey != NULL); *subkey = NULL; + /* * in_creds MUST be a valid credential NOT just a partially filled in * place holder for us to get credentials for the caller. @@ -215,8 +225,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, /* Get the encryption types list */ if (ktypes) { - /* Check passed ktypes and make sure they're valid. */ - for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { + /* Check passed ktypes and make sure they're valid. */ + for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) { if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes])) return KRB5_PROG_ETYPE_NOSUPP; } @@ -236,6 +246,8 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, } else tgsreq.second_ticket = 0; + ap_req_padata.contents = NULL; + /* encode the body; then checksum it */ if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch))) goto send_tgs_error_2; @@ -250,47 +262,74 @@ krb5int_send_tgs(krb5_context context, krb5_flags kdcoptions, } krb5_free_data(context, scratch); - ap_req_padata.pa_type = KRB5_PADATA_AP_REQ; - ap_req_padata.length = scratch2.length; - ap_req_padata.contents = (krb5_octet *)scratch2.data; - - /* combine in any other supplied padata */ + tgsreq.padata = (krb5_pa_data **)calloc(2, sizeof(krb5_pa_data *)); + if (tgsreq.padata == NULL) { + free(scratch2.data); + goto send_tgs_error_2; + } + tgsreq.padata[0] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data)); + if (tgsreq.padata[0] == NULL) { + free(scratch2.data); + goto send_tgs_error_2; + } + tgsreq.padata[0]->pa_type = KRB5_PADATA_AP_REQ; + tgsreq.padata[0]->length = scratch2.length; + tgsreq.padata[0]->contents = (krb5_octet *)scratch2.data; + tgsreq.padata[1] = NULL; + + /* combine in any other supplied padata, unfortunately now it is + * necessary to copy it as the callback function might modify the + * padata, and having a separate path for the non-callback case, + * or attempting to determine which elements were changed by the + * callback, would have complicated the code significantly. + */ if (padata) { - krb5_pa_data * const * counter; - register unsigned int i = 0; - for (counter = padata; *counter; counter++, i++); - combined_padata = malloc((i+2) * sizeof(*combined_padata)); - if (!combined_padata) { - free(ap_req_padata.contents); - retval = ENOMEM; - goto send_tgs_error_2; - } - combined_padata[0] = &ap_req_padata; - for (i = 1, counter = padata; *counter; counter++, i++) - combined_padata[i] = (krb5_pa_data *) *counter; - combined_padata[i] = 0; - } else { - combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata)); - if (!combined_padata) { - free(ap_req_padata.contents); - retval = ENOMEM; + krb5_pa_data **tmp; + int i; + + for (i = 0; padata[i]; i++) + ; + + tmp = (krb5_pa_data **)realloc(tgsreq.padata, + (i + 2) * sizeof(*combined_padata)); + if (tmp == NULL) goto send_tgs_error_2; + + tgsreq.padata = tmp; + + for (i = 0; padata[i]; i++) { + krb5_pa_data *pa; + + pa = tgsreq.padata[1 + i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data)); + if (tgsreq.padata == NULL) { + retval = ENOMEM; + goto send_tgs_error_2; + } + + pa->pa_type = padata[i]->pa_type; + pa->length = padata[i]->length; + pa->contents = (krb5_octet *)malloc(padata[i]->length); + if (pa->contents == NULL) { + retval = ENOMEM; + goto send_tgs_error_2; + } + memcpy(pa->contents, padata[i]->contents, padata[i]->length); } - combined_padata[0] = &ap_req_padata; - combined_padata[1] = 0; + tgsreq.padata[1 + i] = NULL; } - tgsreq.padata = combined_padata; + if (pacb_fct != NULL) { + if ((retval = (*pacb_fct)(context, local_subkey, &tgsreq, pacb_data))) + goto send_tgs_error_2; + } /* the TGS_REQ is assembled in tgsreq, so encode it */ - if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) { - free(ap_req_padata.contents); - free(combined_padata); + if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) goto send_tgs_error_2; - } - free(ap_req_padata.contents); - free(combined_padata); /* now send request & get response from KDC */ + krb5_free_pa_data(context, tgsreq.padata); + tgsreq.padata = NULL; + send_again: use_master = 0; retval = krb5_sendto_kdc(context, scratch, @@ -325,6 +364,8 @@ send_again: krb5_free_data(context, scratch); send_tgs_error_2:; + if (tgsreq.padata) + krb5_free_pa_data(context, tgsreq.padata); if (sec_ticket) krb5_free_ticket(context, sec_ticket); diff --git a/src/lib/krb5/krb/srv_dec_tkt.c b/src/lib/krb5/krb/srv_dec_tkt.c index b5cf260f2c..0934e27e10 100644 --- a/src/lib/krb5/krb/srv_dec_tkt.c +++ b/src/lib/krb5/krb/srv_dec_tkt.c @@ -70,27 +70,70 @@ krb5int_server_decrypt_ticket_keyblock(krb5_context context, } -krb5_error_code KRB5_CALLCONV +krb5_error_code KRB5_CALLCONV krb5_server_decrypt_ticket_keytab(krb5_context context, - const krb5_keytab kt, + const krb5_keytab keytab, krb5_ticket *ticket) { - krb5_error_code retval; - krb5_enctype enctype; - krb5_keytab_entry ktent; + krb5_error_code retval; + krb5_keytab_entry ktent; + + retval = KRB5_KT_NOTFOUND; + + if (keytab->ops->start_seq_get == NULL) { + retval = krb5_kt_get_entry(context, keytab, + ticket->server, + ticket->enc_part.kvno, + ticket->enc_part.enctype, &ktent); + if (retval == 0) { + retval = krb5int_server_decrypt_ticket_keyblock(context, &ktent.key, ticket); + + (void) krb5_free_keytab_entry_contents(context, &ktent); + } + } else { + krb5_error_code code; + krb5_kt_cursor cursor; + + retval = krb5_kt_start_seq_get(context, keytab, &cursor); + if (retval != 0) + goto map_error; - enctype = ticket->enc_part.enctype; + while ((code = krb5_kt_next_entry(context, keytab, + &ktent, &cursor)) == 0) { + if (ktent.key.enctype != ticket->enc_part.enctype) + continue; - if ((retval = krb5_kt_get_entry(context, kt, ticket->server, - ticket->enc_part.kvno, - enctype, &ktent))) - return retval; + retval = krb5int_server_decrypt_ticket_keyblock(context, &ktent.key, ticket); + if (retval == 0) { + krb5_principal tmp; - retval = krb5int_server_decrypt_ticket_keyblock(context, - &ktent.key, ticket); - /* Upon error, Free keytab entry first, then return */ + retval = krb5_copy_principal(context, ktent.principal, &tmp); + if (retval == 0) { + krb5_free_principal(context, ticket->server); + ticket->server = tmp; + } + (void) krb5_free_keytab_entry_contents(context, &ktent); + break; + } + (void) krb5_free_keytab_entry_contents(context, &ktent); + } + + code = krb5_kt_end_seq_get(context, keytab, &cursor); + if (code != 0) + retval = code; + } + +map_error: + switch (retval) { + case KRB5_KT_KVNONOTFOUND: + case KRB5_KT_NOTFOUND: + case KRB5KRB_AP_ERR_BAD_INTEGRITY: + retval = KRB5KRB_AP_WRONG_PRINC; + break; + default: + break; + } - (void) krb5_kt_free_entry(context, &ktent); return retval; } #endif /* LEAN_CLIENT */ diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports index bbefc942f3..cf4dc21ec7 100644 --- a/src/lib/krb5/libkrb5.exports +++ b/src/lib/krb5/libkrb5.exports @@ -21,11 +21,12 @@ decode_krb5_error decode_krb5_etype_info decode_krb5_etype_info2 decode_krb5_fast_req -decode_krb5_pa_fx_fast_request decode_krb5_kdc_req_body decode_krb5_pa_enc_ts decode_krb5_pa_for_user +decode_krb5_pa_fx_fast_request decode_krb5_pa_pac_req +decode_krb5_pa_s4u_x509_user decode_krb5_padata_sequence decode_krb5_predicted_sam_response decode_krb5_priv @@ -62,10 +63,11 @@ encode_krb5_error encode_krb5_etype_info encode_krb5_etype_info2 encode_krb5_fast_response -encode_krb5_pa_fx_fast_reply encode_krb5_kdc_req_body encode_krb5_pa_enc_ts encode_krb5_pa_for_user +encode_krb5_pa_fx_fast_reply +encode_krb5_pa_s4u_x509_user encode_krb5_pa_server_referral_data encode_krb5_pa_svr_referral_data encode_krb5_padata_sequence @@ -73,6 +75,7 @@ encode_krb5_predicted_sam_response encode_krb5_priv encode_krb5_pwd_data encode_krb5_pwd_sequence +encode_krb5_s4u_userid encode_krb5_safe encode_krb5_sam_challenge encode_krb5_sam_key @@ -149,9 +152,9 @@ krb5_authdata_export_internal krb5_authdata_free_internal krb5_authdata_import_attributes krb5_build_principal +krb5_build_principal_alloc_va krb5_build_principal_ext krb5_build_principal_va -krb5_build_principal_alloc_va krb5_cc_close krb5_cc_copy_creds krb5_cc_default @@ -259,8 +262,9 @@ krb5_free_ktypes krb5_free_last_req krb5_free_pa_data krb5_free_pa_enc_ts -krb5_free_pa_pac_req krb5_free_pa_for_user +krb5_free_pa_pac_req +krb5_free_pa_s4u_x509_user krb5_free_pa_server_referral_data krb5_free_pa_svr_referral_data krb5_free_passwd_phrase_element @@ -300,6 +304,8 @@ krb5_get_cred_from_kdc_renew krb5_get_cred_from_kdc_validate krb5_get_cred_via_tkt krb5_get_credentials +krb5_get_credentials_for_proxy +krb5_get_credentials_for_user krb5_get_credentials_renew krb5_get_credentials_validate krb5_get_default_config_files @@ -397,7 +403,6 @@ krb5_os_free_context krb5_os_hostaddr krb5_os_init_context krb5_os_localaddr -krb5int_get_domain_realm_mapping krb5_overridekeyname krb5_pac_add_buffer krb5_pac_free @@ -548,6 +553,7 @@ krb5int_find_pa_data krb5int_foreach_localaddr krb5int_free_addrlist krb5int_free_data_list +krb5int_get_domain_realm_mapping krb5int_init_context_kdc krb5int_initialize_library krb5int_pac_sign diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index dcf08d996b..3e5f9e2341 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -57,7 +57,7 @@ #define DEFAULT_UDP_PREF_LIMIT 1465 #define HARD_UDP_LIMIT 32700 /* could probably do 64K-epsilon ? */ -#undef DEBUG +#define DEBUG 1 #ifdef DEBUG int krb5int_debug_sendto_kdc = 0; diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c index 8b3c7a14a3..1cf67629bc 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal.c @@ -188,7 +188,10 @@ krb5_ldap_iterate(context, match_expr, func, func_arg) LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes); for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) { - if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) { + values=ldap_get_values(ld, ent, "krbcanonicalname"); + if (values == NULL) + values=ldap_get_values(ld, ent, "krbprincipalname"); + if (values != NULL) { for (i=0; values[i] != NULL; ++i) { if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0) continue; @@ -201,13 +204,11 @@ krb5_ldap_iterate(context, match_expr, func, func_arg) (*func)(func_arg, &entry); krb5_dbe_free_contents(context, &entry); (void) krb5_free_principal(context, principal); - if (princ_name) - free(princ_name); + free(princ_name); break; } (void) krb5_free_principal(context, principal); - if (princ_name) - free(princ_name); + free(princ_name); } ldap_value_free(values); } diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c index 14d029c452..03c3da48d7 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c @@ -69,6 +69,30 @@ berval2tl_data(struct berval *in, krb5_tl_data **out) return 0; } +/* Return true if it's okay to return aliases according to flags. */ +static krb5_boolean +aliases_ok(unsigned int flags) +{ + /* + * The current DAL does not have a flag to indicate whether + * aliases are okay. For service name lookups (AS or TGT path), + * we can always return aliases. For client name lookups, we can + * only return aliases if the client passed the canonicalize flag. + * We abuse the CLIENT_REFERRALS_ONLY flag to detect client name + * lookups. + * + * This method has the side effect of permitting aliases for + * lookups by administrative interfaces (e.g. kadmin). Since we + * don't have explicit admin support for aliases yet, this is + * okay. + */ + if (!(flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY)) + return TRUE; + if (flags & KRB5_KDB_FLAG_CANONICALIZE) + return TRUE; + return FALSE; +} + /* * look up a principal in the directory. */ @@ -160,7 +184,7 @@ krb5_ldap_get_principal(context, searchfor, flags, entries, nentries, more) if ((values=ldap_get_values(ld, ent, "krbcanonicalname")) != NULL) { if (values[0] && strcmp(values[0], user) != 0) { /* We matched an alias, not the canonical name. */ - if (flags & KRB5_KDB_FLAG_CANONICALIZE) { + if (aliases_ok(flags)) { st = krb5_ldap_parse_principal_name(values[0], &cname); if (st != 0) goto cleanup; diff --git a/src/slave/kproplog.c b/src/slave/kproplog.c index 43a7738d41..6fb2e2288f 100644 --- a/src/slave/kproplog.c +++ b/src/slave/kproplog.c @@ -40,23 +40,31 @@ static void print_flags(unsigned int flags) { unsigned int i; - 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 */ - "SUPPORT_DESMD5", /* 0x00004000 */ - "NEW_PRINC", /* 0x00008000 */ + 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 */ + "SUPPORT_DESMD5", /* 0x00004000 */ + "NEW_PRINC", /* 0x00008000 */ + "UNKNOWN_0x00010000", /* 0x00010000 */ + "UNKNOWN_0x00020000", /* 0x00020000 */ + "UNKNOWN_0x00040000", /* 0x00040000 */ + "UNKNOWN_0x00080000", /* 0x00080000 */ + "OK_AS_DELEGATE", /* 0x00100000 */ + "OK_TO_AUTH_AS_DELEGATE", /* 0x00200000 */ + "NO_AUTH_DATA_REQUIRED", /* 0x00400000 */ + }; for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { @@ -169,7 +177,7 @@ print_key(kdbe_key_t *k) for (i = 0; i < k->k_enctype.k_enctype_len; i++) { printf("\t\t\tenc type: 0x%x\n", - k->k_enctype.k_enctype_val[i]); + k->k_enctype.k_enctype_val[i]); } str = k->k_contents.k_contents_val; diff --git a/src/tests/asn.1/krb5_decode_leak.c b/src/tests/asn.1/krb5_decode_leak.c index 3f8b929ac7..3eb6f3c66e 100644 --- a/src/tests/asn.1/krb5_decode_leak.c +++ b/src/tests/asn.1/krb5_decode_leak.c @@ -659,10 +659,20 @@ main(int argc, char **argv) ktest_empty_enc_sam_response_enc_2(&sam_ch2); } /****************************************************************/ + /* encode_krb5_pa_s4u_x509_user */ + { + krb5_pa_s4u_x509_user s4u, *tmp; + setup(s4u, "pa_s4u_x509_user", + ktest_make_sample_pa_s4u_x509_user); + leak_test(s4u, encode_krb5_pa_s4u_x509_user, + decode_krb5_pa_s4u_x509_user, + krb5_free_pa_s4u_x509_user); + ktest_empty_pa_s4u_x509_user(&s4u); + } + /****************************************************************/ /* encode_krb5_ad_kdcissued */ { krb5_ad_kdcissued kdci, *tmp; - setup(kdci, "ad_kdcissued", ktest_make_sample_ad_kdcissued); leak_test(kdci, encode_krb5_ad_kdcissued, diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c index 90cf747e56..401b26240e 100644 --- a/src/tests/asn.1/krb5_decode_test.c +++ b/src/tests/asn.1/krb5_decode_test.c @@ -890,11 +890,23 @@ int main(argc, argv) ktest_empty_sam_response(&ref); } + + /****************************************************************/ + /* decode_pa_s4u_x509_user */ + { + setup(krb5_pa_s4u_x509_user,"krb5_pa_s4u_x509_user",ktest_make_sample_pa_s4u_x509_user); + decode_run("pa_s4u_x509_user","","30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34",decode_krb5_pa_s4u_x509_user,ktest_equal_pa_s4u_x509_user,krb5_free_pa_s4u_x509_user); + ktest_empty_pa_s4u_x509_user(&ref); + } + + /****************************************************************/ + /* decode_ad_kdcissued */ { setup(krb5_ad_kdcissued,"krb5_ad_kdcissued",ktest_make_sample_ad_kdcissued); decode_run("ad_kdcissued","","30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72",decode_krb5_ad_kdcissued,ktest_equal_ad_kdcissued,krb5_free_ad_kdcissued); ktest_empty_ad_kdcissued(&ref); } + #ifdef ENABLE_LDAP /* ldap sequence_of_keys */ { diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c index a4896d96a7..c010af9ab6 100644 --- a/src/tests/asn.1/krb5_encode_test.c +++ b/src/tests/asn.1/krb5_encode_test.c @@ -696,6 +696,17 @@ main(argc, argv) ktest_empty_enc_sam_response_enc_2(&sam_ch2); } /****************************************************************/ + /* encode_krb5_pa_s4u_x509_user */ + { + krb5_pa_s4u_x509_user s4u; + setup(s4u,krb5_pa_s4u_x509_user,"pa_s4u_x509_user", + ktest_make_sample_pa_s4u_x509_user); + encode_run(s4u,krb5_pa_s4u_x509_user, + "pa_s4u_x509_user","", + encode_krb5_pa_s4u_x509_user); + ktest_empty_pa_s4u_x509_user(&s4u); + } + /****************************************************************/ /* encode_krb5_ad_kdcissued */ { krb5_ad_kdcissued kdci; diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c index f7814f8d9a..f41347c0f8 100644 --- a/src/tests/asn.1/ktest.c +++ b/src/tests/asn.1/ktest.c @@ -825,6 +825,23 @@ krb5_error_code ktest_make_sample_enc_sam_response_enc_2(p) return 0; } +krb5_error_code ktest_make_sample_pa_s4u_x509_user(p) + krb5_pa_s4u_x509_user *p; +{ + krb5_error_code retval; + krb5_s4u_userid *u = &p->user_id; + u->nonce = 13243546; + retval = ktest_make_sample_principal(&u->user); + if (retval) return retval; + u->subject_cert.data = strdup("pa_s4u_x509_user"); + if (u->subject_cert.data == NULL) return ENOMEM; + u->subject_cert.length = strlen(u->subject_cert.data); + u->options = 0x80000000; + retval = ktest_make_sample_checksum(&p->cksum); + if (retval) return retval; + return 0; +} + krb5_error_code ktest_make_sample_ad_kdcissued(p) krb5_ad_kdcissued *p; { @@ -1433,6 +1450,14 @@ void ktest_empty_enc_sam_response_enc_2(p) ktest_empty_data(&p->sam_sad); } +void ktest_empty_pa_s4u_x509_user(p) + krb5_pa_s4u_x509_user *p; +{ + ktest_destroy_principal(&p->user_id.user); + ktest_empty_data(&p->user_id.subject_cert); + if (p->cksum.contents) free(p->cksum.contents); +} + void ktest_empty_ad_kdcissued(p) krb5_ad_kdcissued *p; { diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h index d02b567a00..fa33ceffd4 100644 --- a/src/tests/asn.1/ktest.h +++ b/src/tests/asn.1/ktest.h @@ -105,6 +105,7 @@ krb5_error_code ktest_make_sample_enc_sam_response_enc (krb5_enc_sam_response_enc *p); krb5_error_code ktest_make_sample_predicted_sam_response(krb5_predicted_sam_response *p); krb5_error_code ktest_make_sample_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p); +krb5_error_code ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p); krb5_error_code ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p); #ifdef ENABLE_LDAP @@ -214,6 +215,7 @@ void ktest_empty_enc_sam_response_enc(krb5_enc_sam_response_enc *p); void ktest_empty_predicted_sam_response(krb5_predicted_sam_response *p); void ktest_empty_sam_response_2(krb5_sam_response_2 *p); void ktest_empty_enc_sam_response_enc_2(krb5_enc_sam_response_enc_2 *p); +void ktest_empty_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p); void ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p); #ifdef ENABLE_LDAP diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c index a23e82bc7d..5479f8047a 100644 --- a/src/tests/asn.1/ktest_equal.c +++ b/src/tests/asn.1/ktest_equal.c @@ -542,6 +542,21 @@ int ktest_equal_sam_response(ref, var) return p; } +int ktest_equal_pa_s4u_x509_user(ref, var) + krb5_pa_s4u_x509_user *ref; + krb5_pa_s4u_x509_user *var; +{ + int p = TRUE; + if (ref == var) return TRUE; + else if (ref == NULL || var == NULL) return FALSE; + p=p&&scalar_equal(user_id.nonce); + p=p&&ptr_equal(user_id.user,ktest_equal_principal_data); + p=p&&struct_equal(user_id.subject_cert,ktest_equal_data); + p=p&&scalar_equal(user_id.options); + p=p&&struct_equal(cksum,ktest_equal_checksum); + return p; +} + int ktest_equal_ad_kdcissued(ref, var) krb5_ad_kdcissued *ref; krb5_ad_kdcissued *var; diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h index cd92453478..1464ebb509 100644 --- a/src/tests/asn.1/ktest_equal.h +++ b/src/tests/asn.1/ktest_equal.h @@ -91,6 +91,10 @@ int ktest_equal_krb5_etype_info_entry (krb5_etype_info_entry * ref, krb5_etype_info_entry * var); +int ktest_equal_pa_s4u_x509_user + (krb5_pa_s4u_x509_user *ref, + krb5_pa_s4u_x509_user *var); + int ktest_equal_ad_kdcissued (krb5_ad_kdcissued *ref, krb5_ad_kdcissued *var); diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out index 8360eba3de..952e69c771 100644 --- a/src/tests/asn.1/reference_encode.out +++ b/src/tests/asn.1/reference_encode.out @@ -56,4 +56,5 @@ encode_krb5_enc_sam_response_enc: 30 38 A0 05 02 03 01 33 2A A1 11 18 0F 31 39 3 encode_krb5_predicted_sam_response: 30 6D A0 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A1 07 03 05 00 00 00 00 09 A2 11 18 0F 31 39 37 30 30 31 30 31 30 30 30 30 31 37 5A A3 03 02 01 12 A4 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A5 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A6 07 04 05 68 65 6C 6C 6F encode_krb5_sam_response_2: 30 42 A0 03 02 01 2B A1 07 03 05 00 80 00 00 00 A2 0C 04 0A 74 72 61 63 6B 20 64 61 74 61 A3 1D 30 1B A0 03 02 01 01 A1 04 02 02 0D 36 A2 0E 04 0C 6E 6F 6E 63 65 20 6F 72 20 73 61 64 A4 05 02 03 54 32 10 encode_krb5_enc_sam_response_enc_2: 30 1F A0 03 02 01 58 A1 18 04 16 65 6E 63 5F 73 61 6D 5F 72 65 73 70 6F 6E 73 65 5F 65 6E 63 5F 32 +encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out index 09ffb0f665..b19ca747e7 100644 --- a/src/tests/asn.1/trval_reference.out +++ b/src/tests/asn.1/trval_reference.out @@ -1246,6 +1246,23 @@ encode_krb5_enc_sam_response_enc_2: . [0] [Integer] 88 . [1] [Octet String] "enc_sam_response_enc_2" +encode_krb5_pa_s4u_x509_user: + +[Sequence/Sequence Of] +. [0] [Sequence/Sequence Of] +. . [0] [Integer] 13243546 +. . [1] [Sequence/Sequence Of] +. . . [0] [Integer] 1 +. . . [1] [Sequence/Sequence Of] +. . . . [General string] "hftsai" +. . . . [General string] "extra" +. . [2] [General string] "ATHENA.MIT.EDU" +. . [3] [Octet String] "pa_s4u_x509_user" +. . [4] [Bit String] 0x80000000 +. [1] [Sequence/Sequence Of] +. . [0] [Integer] 1 +. . [1] [Octet String] "1234" + encode_krb5_ad_kdcissued: [Sequence/Sequence Of] diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in index 455fbc0935..fd7b7db5b6 100644 --- a/src/tests/gssapi/Makefile.in +++ b/src/tests/gssapi/Makefile.in @@ -6,17 +6,19 @@ DEFINES = -DUSE_AUTOCONF_H PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_namingexts.c +SRCS= $(srcdir)/t_imp_name.c $(srcdir)/t_s4u.c $(srcdir)/t_namingexts.c -OBJS= t_imp_name.o t_namingexts.o +OBJS= t_imp_name.o t_s4u.o t_namingexts.o -all:: t_imp_name t_namingexts +all:: t_imp_name t_s4u t_namingexts t_imp_name: t_imp_name.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_imp_name t_imp_name.o $(GSS_LIBS) $(KRB5_BASE_LIBS) t_namingexts: t_namingexts.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(CC_LINK) -o t_namingexts t_namingexts.o $(GSS_LIBS) $(KRB5_BASE_LIBS) +t_s4u: t_s4u.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_s4u t_s4u.o $(GSS_LIBS) $(KRB5_BASE_LIBS) clean:: - $(RM) t_imp_name + $(RM) t_imp_name t_s4u t_namingexts diff --git a/src/tests/gssapi/t_s4u.c b/src/tests/gssapi/t_s4u.c new file mode 100644 index 0000000000..264e60a605 --- /dev/null +++ b/src/tests/gssapi/t_s4u.c @@ -0,0 +1,418 @@ +/* -*- mode: c; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#include +#include +#include + +#include + +/* + * Test program for protocol transition (S4U2Self) and constrained delegation + * (S4U2Proxy) + * + * Note: because of name canonicalization, the following tips may help + * when configuring with Active Directory: + * + * - Create a computer account FOO$ + * - Set the UPN to host/foo.domain (no suffix); this is necessary to + * be able to send an AS-REQ as this principal, otherwise you would + * need to use the canonical name (FOO$), which will cause principal + * comparison errors in gss_accept_sec_context(). + * - Add a SPN of host/foo.domain + * - Configure the computer account to support constrained delegation with + * protocol transition (Trust this computer for delegation to specified + * services only / Use any authentication protocol) + * - Add host/foo.domain to the keytab (possibly easiest to do this + * with ktadd) + * + * For S4U2Proxy to work the TGT must be forwardable too. + * + * Usage eg: + * + * kinit -k -t test.keytab -f 'host/test.win.mit.edu@WIN.MIT.EDU' + * ./t_s4u delegtest@WIN.MIT.EDU HOST/WIN-EQ7E4AA2WR8.win.mit.edu@WIN.MIT.EDU test.keytab + */ + +static gss_OID_desc spnego_mech = { 6, "\053\006\001\005\005\002" }; + +int use_spnego = 0; + +static void displayStatus_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); + fprintf(stderr, "%s: %s\n", m, (char *)msg.value); + (void) gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } +} + +static void displayStatus(msg, maj_stat, min_stat) + char *msg; + OM_uint32 maj_stat; + OM_uint32 min_stat; +{ + displayStatus_1(msg, maj_stat, GSS_C_GSS_CODE); + displayStatus_1(msg, min_stat, GSS_C_MECH_CODE); +} + +static OM_uint32 +displayCanonName(OM_uint32 *minor, gss_name_t name, char *tag) +{ + gss_name_t canon; + OM_uint32 major, tmp_minor; + gss_buffer_desc buf; + + major = gss_canonicalize_name(minor, name, + (gss_OID)gss_mech_krb5, &canon); + if (GSS_ERROR(major)) { + displayStatus("gss_canonicalize_name", major, *minor); + return major; + } + + major = gss_display_name(minor, canon, &buf, NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_display_name", major, *minor); + gss_release_name(&tmp_minor, &canon); + return major; + } + + printf("%s:\t%s\n", tag, (char *)buf.value); + + gss_release_buffer(&tmp_minor, &buf); + gss_release_name(&tmp_minor, &canon); + + return GSS_S_COMPLETE; +} + +static OM_uint32 +displayOID(OM_uint32 *minor, gss_OID oid, char *tag) +{ + OM_uint32 major, tmp_minor; + gss_buffer_desc buf; + + major = gss_oid_to_str(minor, oid, &buf); + if (GSS_ERROR(major)) { + displayStatus("gss_oid_to_str", major, *minor); + return major; + } + + printf("%s:\t%s\n", tag, (char *)buf.value); + + gss_release_buffer(&tmp_minor, &buf); + + return GSS_S_COMPLETE; +} + +static OM_uint32 +initAcceptSecContext(OM_uint32 *minor, + gss_cred_id_t claimant_cred_handle, + gss_cred_id_t verifier_cred_handle, + gss_cred_id_t *deleg_cred_handle) +{ + OM_uint32 major, tmp_minor; + gss_buffer_desc token, tmp; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT; + gss_name_t source_name = GSS_C_NO_NAME; + gss_name_t target_name = GSS_C_NO_NAME; + OM_uint32 time_rec; + gss_OID mech = GSS_C_NO_OID; + + token.value = NULL; + token.length = 0; + + tmp.value = NULL; + tmp.length = 0; + + *deleg_cred_handle = GSS_C_NO_CREDENTIAL; + + major = gss_inquire_cred(minor, verifier_cred_handle, + &target_name, NULL, NULL, NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_inquire_cred", major, *minor); + return major; + } + + displayCanonName(minor, target_name, "Target name"); + + mech = use_spnego ? (gss_OID)&spnego_mech : (gss_OID)gss_mech_krb5; + displayOID(minor, mech, "Target mech"); + + major = gss_init_sec_context(minor, + claimant_cred_handle, + &initiator_context, + target_name, + mech, + GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + GSS_C_NO_BUFFER, + NULL, + &token, + NULL, + &time_rec); + + if (target_name != GSS_C_NO_NAME) + (void) gss_release_name(&tmp_minor, &target_name); + + if (GSS_ERROR(major)) { + displayStatus("gss_init_sec_context", major, *minor); + return major; + } + + (void) gss_delete_sec_context(minor, &initiator_context, NULL); + mech = GSS_C_NO_OID; + + major = gss_accept_sec_context(minor, + &acceptor_context, + verifier_cred_handle, + &token, + GSS_C_NO_CHANNEL_BINDINGS, + &source_name, + &mech, + &tmp, + NULL, + &time_rec, + deleg_cred_handle); + + if (GSS_ERROR(major)) + displayStatus("gss_accept_sec_context", major, *minor); + else { + displayCanonName(minor, source_name, "Source name"); + displayOID(minor, mech, "Source mech"); + } + + (void) gss_release_name(&tmp_minor, &source_name); + (void) gss_delete_sec_context(&tmp_minor, &acceptor_context, NULL); + (void) gss_release_buffer(&tmp_minor, &token); + (void) gss_release_buffer(&tmp_minor, &tmp); + (void) gss_release_oid(&tmp_minor, &mech); + + return major; +} + +static OM_uint32 +constrainedDelegate(OM_uint32 *minor, + gss_OID_set desired_mechs, + gss_name_t target, + gss_cred_id_t delegated_cred_handle, + gss_cred_id_t verifier_cred_handle) +{ + OM_uint32 major, tmp_minor; + gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT; + gss_name_t cred_name = GSS_C_NO_NAME; + OM_uint32 time_rec, lifetime; + gss_cred_usage_t usage; + gss_buffer_desc token; + gss_OID_set mechs; + + printf("Constrained delegation tests follow\n"); + printf("-----------------------------------\n\n"); + + if (gss_inquire_cred(minor, verifier_cred_handle, &cred_name, + &lifetime, &usage, NULL) == GSS_S_COMPLETE) { + displayCanonName(minor, cred_name, "Proxy name"); + gss_release_name(&tmp_minor, &cred_name); + } + displayCanonName(minor, target, "Target name"); + if (gss_inquire_cred(minor, delegated_cred_handle, &cred_name, + &lifetime, &usage, &mechs) == GSS_S_COMPLETE) { + displayCanonName(minor, cred_name, "Delegated name"); + displayOID(minor, &mechs->elements[0], "Delegated mech"); + gss_release_name(&tmp_minor, &cred_name); + } + + printf("\n"); + + major = gss_init_sec_context(minor, + delegated_cred_handle, + &initiator_context, + target, + mechs ? &mechs->elements[0] : + (gss_OID)gss_mech_krb5, + GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + GSS_C_NO_BUFFER, + NULL, + &token, + NULL, + &time_rec); + if (GSS_ERROR(major)) + displayStatus("gss_init_sec_context", major, *minor); + + (void) gss_release_buffer(&tmp_minor, &token); + (void) gss_delete_sec_context(&tmp_minor, &initiator_context, NULL); + (void) gss_release_oid_set(&tmp_minor, &mechs); + + return major; +} + +int main(int argc, char *argv[]) +{ + OM_uint32 minor, major; + gss_cred_id_t impersonator_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t user_cred_handle = GSS_C_NO_CREDENTIAL; + gss_cred_id_t delegated_cred_handle = GSS_C_NO_CREDENTIAL; + gss_name_t user = GSS_C_NO_NAME, target = GSS_C_NO_NAME; + gss_OID_set_desc mechs; + gss_OID_set actual_mechs = GSS_C_NO_OID_SET; + gss_buffer_desc buf; + + if (argc < 2 || argc > 5) { + fprintf(stderr, "Usage: %s [--spnego] [user] " + "[proxy-target] [keytab]\n", argv[0]); + fprintf(stderr, " proxy-target and keytab are optional\n"); + exit(1); + } + + if (strcmp(argv[1], "--spnego") == 0) { + use_spnego++; + argc--; + argv++; + } + + buf.value = argv[1]; + buf.length = strlen((char *)buf.value); + + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &user); + if (GSS_ERROR(major)) { + displayStatus("gss_import_name(user)", major, minor); + goto out; + } + + if (argc > 2 && strcmp(argv[2], "-")) { + buf.value = argv[2]; + buf.length = strlen((char *)buf.value); + + major = gss_import_name(&minor, &buf, + (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, + &target); + if (GSS_ERROR(major)) { + displayStatus("gss_import_name(target)", major, minor); + goto out; + } + } else { + target = GSS_C_NO_NAME; + } + + if (argc > 3) { + major = krb5_gss_register_acceptor_identity(argv[3]); + if (GSS_ERROR(major)) { + displayStatus("krb5_gss_register_acceptor_identity", + major, minor); + goto out; + } + } + + mechs.elements = use_spnego ? (gss_OID)&spnego_mech : + (gss_OID)gss_mech_krb5; + mechs.count = 1; + + /* get default cred */ + major = gss_acquire_cred(&minor, + GSS_C_NO_NAME, + GSS_C_INDEFINITE, + &mechs, + GSS_C_BOTH, + &impersonator_cred_handle, + &actual_mechs, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred", major, minor); + goto out; + } + + (void) gss_release_oid_set(&minor, &actual_mechs); + + printf("Protocol transition tests follow\n"); + printf("-----------------------------------\n\n"); + + /* get S4U2Self cred */ + major = gss_acquire_cred_impersonate_name(&minor, + impersonator_cred_handle, + user, + GSS_C_INDEFINITE, + &mechs, + GSS_C_INITIATE, + &user_cred_handle, + &actual_mechs, + NULL); + if (GSS_ERROR(major)) { + displayStatus("gss_acquire_cred_impersonate_name", major, minor); + goto out; + } + + major = initAcceptSecContext(&minor, + user_cred_handle, + impersonator_cred_handle, + &delegated_cred_handle); + if (GSS_ERROR(major)) + goto out; + + printf("\n"); + + if (target != GSS_C_NO_NAME && + delegated_cred_handle != GSS_C_NO_CREDENTIAL) { + major = constrainedDelegate(&minor, &mechs, target, + delegated_cred_handle, + impersonator_cred_handle); + } else if (target != GSS_C_NO_NAME) { + fprintf(stderr, "Warning: no delegated credentials handle returned\n\n"); + fprintf(stderr, "Verify:\n\n"); + fprintf(stderr, " - The TGT for the impersonating service is forwardable\n"); + fprintf(stderr, " - The T2A4D flag set on the impersonating service's UAC\n"); + fprintf(stderr, " - The user is not marked sensitive and cannot be delegated\n"); + fprintf(stderr, "\n"); + } + +out: + (void) gss_release_name(&minor, &user); + (void) gss_release_name(&minor, &target); + (void) gss_release_cred(&minor, &delegated_cred_handle); + (void) gss_release_cred(&minor, &impersonator_cred_handle); + (void) gss_release_cred(&minor, &user_cred_handle); + (void) gss_release_oid_set(&minor, &actual_mechs); + + return GSS_ERROR(major) ? 1 : 0; +} + diff --git a/src/tests/mk_migr/db2_backend/README_for_mkmdb2 b/src/tests/mk_migr/db2_backend/README_for_mkmdb2 new file mode 100644 index 0000000000..1a5cf30e19 --- /dev/null +++ b/src/tests/mk_migr/db2_backend/README_for_mkmdb2 @@ -0,0 +1,37 @@ +ABOUT: +A translation of Will Fiveash's "mit_db2_mkey_migrate_testB" ksh code into Python with the default db2 backend. With minor fixes and changes. Written by HaoQi Li. + +DEFAULT SETTINGS: +Options Name Default Setting + -h Help + -v Verbose: True + -p Testing pw: test123 + -s Sandbox loc: src/tests/mk_migr/db2_backend/sandbox + -c Krb5kdc: src/kdc/krb5kdc + -d Kadmind: src/kadmin/server/kadmind + -b Kdb5_util: src/kadmin/dbutil/kdb5_util + -l Kadmin.local: src/kadmin/cli/kadmin.local + -n Kadmin: src/kadmin/cli/kadmin + -t Client paths: src/clients + +INPUTS: +* src/tests/mk_migr/db2_backend/input_conf/kdc_template_db2.conf +* src/tests/mk_migr/db2_backend/input_conf/krb5_template_db2.conf +* src/tests/mk_migr/db2_backend/input_conf/kadm5_template_db2.acl + +OUTPUTS: +* sandbox that contains customized outfile with all commands and their outputs, kdc.conf, krb5.conf, kadm6.acl, and others. +* Statistics on screen of number of commands passed and failed. + +EXAMPLES: +- MUST RUN from trunk/src. +* python tests/mk_migr/db2_backend/mkmdb2.py + Using all Default Settings. +* python tests/mk_migr/db2_backend/mkmdb2.py -s /tmp/mySandbox + Sandbox now can be found in /tmp/mySandbox. + + +# Notes: +# Exists only at the end, unless fatal errors are encountered. Otherwise, skips errors and continue!! +# "haoqili" is a name that can be changed. +# 2019 and 2029 are future dates that should best be written not as fixed. Such as now+10years. diff --git a/src/tests/mk_migr/db2_backend/input_conf/kadm5_template_db2.acl b/src/tests/mk_migr/db2_backend/input_conf/kadm5_template_db2.acl new file mode 100644 index 0000000000..719677a865 --- /dev/null +++ b/src/tests/mk_migr/db2_backend/input_conf/kadm5_template_db2.acl @@ -0,0 +1 @@ +*/admin * diff --git a/src/tests/mk_migr/db2_backend/input_conf/kdc_template_db2.conf b/src/tests/mk_migr/db2_backend/input_conf/kdc_template_db2.conf new file mode 100644 index 0000000000..31ff5220b7 --- /dev/null +++ b/src/tests/mk_migr/db2_backend/input_conf/kdc_template_db2.conf @@ -0,0 +1,14 @@ +[kdcdefaults] + kdc_ports = 8888 + +[realms] + K.MIT.EDU = { + database_name = %(sandboxdir)s/principal + acl_file = %(sandboxdir)s/kadm5.acl + key_stash_file = %(sandboxdir)s/keyStashFile + kdc_ports = 8888 + kpasswd_port = 8887 + kadmind_port = 8886 + max_life = 10h 0m 0s + max_renewable_life = 7d 0h 0m 0s + } diff --git a/src/tests/mk_migr/db2_backend/input_conf/krb5_template_db2.conf b/src/tests/mk_migr/db2_backend/input_conf/krb5_template_db2.conf new file mode 100644 index 0000000000..e89aa872a6 --- /dev/null +++ b/src/tests/mk_migr/db2_backend/input_conf/krb5_template_db2.conf @@ -0,0 +1,21 @@ +[libdefaults] + default_realm = K.MIT.EDU + +[realms] +# use "kdc = ..." if realm admins haven't put SRV records into DNS + K.MIT.EDU = { + admin_server = %(localFQDN)s:8886 + kpasswd_server = %(localFQDN)s:8887 + default_domain = MIT.EDU + kdc = %(localFQDN)s:8888 + v4_instance_convert = { + mit = mit.edu + lithium = lithium.lcs.mit.edu + } + } + ANDREW.CMU.EDU = { + admin_server = vice28.fs.andrew.cmu.edu + } + +[logging] +# kdc = CONSOLE diff --git a/src/tests/mk_migr/db2_backend/mkmdb2.py b/src/tests/mk_migr/db2_backend/mkmdb2.py new file mode 100644 index 0000000000..00ae4cb3bb --- /dev/null +++ b/src/tests/mk_migr/db2_backend/mkmdb2.py @@ -0,0 +1,808 @@ +# Master Key Migration for db2 + +import os, sys, shutil, socket, time, string +from subprocess import Popen, PIPE +from optparse import OptionParser +from time import strftime + +class MasterKeyMigrationTest: + def __init__(self, verbose_in, pw_in, kdcPath_in, kdmdPath_in, kdbPath_in, kdmlPath_in, kdmPath_in, cltPath_in, sandir_in): + self.npass = 0 + self.nfail = 0 + + self.verbose = verbose_in + self.pw = pw_in + + self.krb5kdc = kdcPath_in #1 krb5kdc + self.kadmind = kdmdPath_in #2 kadmind + self.kdb5_util = kdbPath_in #3 kdb5_util + self.kadminlocal = kdmlPath_in #4 kadmin.local + self.kadmin = kdmPath_in #5 kadmin + self.clients = cltPath_in+"/" #6 clients + + self.sandir = sandir_in + + ########## SET UP Write Output File ##### + self.outfile = open(self.sandir+"/outfile", 'w') + + '''print os.environ''' + + def _writeLine(self, astr, prt=False): + self.outfile.write(astr.strip()+"\n") + if prt: + print astr.strip() + + def _writeHeader(self, astr, prt=True): + self.outfile.write("\n========== "+astr.strip()+" ==========\n") + if prt: + print "========== "+astr.strip()+" ==========" + + def _sysexit(self, fatal=False, finished=False): + self._writeLine("++++++++++++++++++++++++++++++", True) + if fatal: + self._writeLine("++++ Test did NOT finish +++++", True) + self._writeLine("++++ FATAL FAILURE! Stopped ++", True) + self._writeLine("++++ See sandbox/outfile +++++", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + sys.exit() + elif not finished: + self._writeLine("++++ Test did NOT finish +++++", True) + self._writeLine("++++ FAIL Detected! keep going", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + else: #finished + self._writeLine("++++ MKM Test Finished +++++++", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + self._writeLine("++++ Commands Passed: %s +++++" % self.npass, True) + self._writeLine("++++ Commands Failed: %s +++++" % self.nfail, True) + sys.exit() + + def _printig(self): + self._writeLine("~.~.~Error should be ignored~.~.~.~") + + def _printerr(self, errm, stderr): + self._writeLine("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#") + self._writeLine("-XX-FAILED: "+errm+". See stderr below:") + [self._writeLine(line) for line in stderr.readlines() ] + + def _printout(self, cmd, pstdout): + if self.verbose: + self._writeLine("---------------------------------------") + self._writeLine("-command: "+cmd) + self._writeLine("-----out: ") + [self._writeLine(line) for line in pstdout.readlines()] + + def _eval(self, succeed, pwait, errm, pstderr, fatal=False, msg2="", finished=False): + if int(pwait) != 0: # is bad + self._printerr(errm, pstderr) + if succeed==True: ## want good + self.nfail += 1 + self._sysexit(fatal, finished) + else: ## want bad + self.npass += 1 + self._printig() + else: # is good + if not succeed: ## want bad + if msg2 != "": + self._writeLine(msg2, True) + self.nfail += 1 + self._sysexit(fatal, finished) + else: ## want good + self.npass += 1 + + def _metafunc(self, command, errmsg, moreinfo="", isLocal=False, succeed=True, fatal=False): + l = command + if isLocal: + pl = Popen(l.split(None,2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + else: + pl = Popen(l.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l+moreinfo, pl.stdout) + + self._eval(succeed, pl.wait(), errmsg, pl.stderr, fatal) + + ########################################### + + # Start the KDC daemons + def _startkdc(self): + self._writeLine("\nstarting kdc daemons ...") + l0 = self.krb5kdc + errm = "error at starting krb5kdc" + self._metafunc(l0, errm) + # below has been changed + l0b = self.kadmind + ' -W -nofork' #the W is for during off strong random numbers + errm = "error at starting kadmind, maybe it's already started" + pl0b = Popen(l0b.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._writeLine( "kadmind -nofork") + started = False + while time.clock() < 3: + l = pl0b.stderr.readline() + if l.find("starting") > -1: + self._writeLine( l.strip()) + self.npass += 1 + started = True + break + else: + self.nfail += 1 + self._printerr("kadmind not starting, check to see if there are any previous kadmind running with cmd: 'ps -ef | grep kadmind' and then do 'sudo kill -9 [# on the left]'", pl0b.stderr) + self._sysexit(fatal=True) + if not started: + self.nfail += 1 + self._sysexit() + self._writeLine("end starting kdc daemons") + + # Kill the KDC daemons in case they are running + def _killkdc(self, suc=True): + l1 = 'pkill -9 -x krb5kdc' + errm = "no krb5kdc killed" + self._metafunc(l1, errm, succeed=suc) + + l2 = 'pkill -9 -x kadmind' + errm = "no kadmind killed" + self._metafunc(l2, errm, succeed=suc) + + # Destroys current database + def _destroykdc(self, suc=True): + l3 = self.kdb5_util+' destroy -f' #forced + errm = "no kdb database destroyed" + self._metafunc(l3, errm, succeed=suc) + + # Create a new database with a new master key + def _createdb(self, pw): + l4 = self.kdb5_util+' -P '+pw+' create -s -W' #added W for svn version 22435 to avoid reading strong random numbers + errm = "error when creating new database, _createdb()" + self._metafunc(l4, errm, fatal=True) + + # Addprinc + def _locAddprinc(self, passw, usern): + l5 = self.kadminlocal+' -q addprinc -pw '+passw+' '+usern + errm = "error when adding princ, _locAddprinc" + self._metafunc(l5, errm, isLocal=True) + + # List princs + def _locListprincs(self): + l6 = self.kadminlocal+' -q listprincs' + errm = "error when listing princs, _locListprincs" + self._metafunc(l6, errm, isLocal=True) + + # Get princs + def _locGetprinc(self, usern, extra=False, succeed=True): + l7 = self.kadminlocal+' -q getprinc '+usern + errm="error when getting princ, _locGetprinc" + + pl7 = Popen(l7.split(None,2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + if not extra: + self._printout(l7, pl7.stdout) + else: + if self.verbose: + self._writeLine("-command: "+l7) + self._writeLine("-----out: ") + for line in pl7.stdout.readlines(): + if line.startswith("Princ") or line.startswith("MKey"): + self._writeLine(line) + self._eval(succeed, pl7.wait(), errm, pl7.stderr) + + # Get princs and finds something in the output + def _locGetprincFind(self, usern, findstr, succeed=True): + l7b = self.kadminlocal+' -q getprinc ' +usern + errm="error when getting princs, _locGetprinc, (regular output of getprincs is not printed here), will NOT continue to find string="+findstr + pl7b = Popen(l7b.split(None, 2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + if self.verbose: + self._writeLine("-command: "+l7b) + if int(pl7b.wait()) != 0: # is bad + self._printerr(errm, pl7b.stderr) + if succeed: ## want good + self.nfail += 1 + self._sysexit() + else: ## want bad + self.npass += 1 + self._printig() + else: # is good + if self.verbose: + self._writeLine( "-----out: ") + boofound = False + for outl in pl7b.stdout.readlines(): + self._writeLine(outl) + if string.find(outl, findstr) > -1: + boofound = True + if boofound: + self._writeLine("----FOUND: "+findstr) + else: + self._writeLine("----NOT FOUND: "+findstr) + if not succeed: ## want bad + self.nfail += 1 + self._sysexit() + else: ## want good + self.npass += 1 + + # Add policy + def _locAddpol(self, maxtime, minlength, minclasses, history, policyname): + rest = "" + if maxtime != None: + rest += '-maxlife '+maxtime+' ' + if minlength != None: + rest += '-minlength '+minlength+' ' + if minclasses != None: + rest += '-minclasses '+minclasses+' ' + if history != None: + rest += '-history '+history+' ' + l8 = self.kadminlocal+' -q add_policy '+rest+policyname + errm = "error when adding policy, _locAddpol" + self._metafunc(l8, errm, isLocal=True) + + # Get pol + def _locGetpol(self, poln): + l8b = self.kadminlocal+' -q getpol '+poln + errm="error when getting pol, _locGetpol" + self._metafunc(l8b, errm, isLocal=True) + + # Modify Principal + def _locModprinc(self, rest): + l9 = self.kadminlocal+' -q modprinc '+rest + errm = "error when modifing principal, _locModprinc" + self._metafunc(l9, errm, isLocal=True) + + # List mkeys + def _listmkeys(self): + l10 = self.kdb5_util+' list_mkeys' + errm = "error when listing mkeys, _listmkeys" + self._metafunc(l10, errm) + + # Use mkeys + def _usemkey(self, kvno, time, succeed=True): + l11 = self.kdb5_util+' use_mkey '+kvno+' '+time + pl11 = Popen(l11.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l11, pl11.stdout) + self._eval(succeed, pl11.wait(), "error when using mkeys, _usemkey", pl11.stderr, msg2="-XX-ERROR: "+l11+" should have failed.") + + + # Change password (cpw) + def _locCpw(self, passw, usern): + l12 = self.kadminlocal+' -q cpw -pw '+passw+' '+usern + errm = "error when changing password, _locCpw" + self._metafunc(l12, errm, moreinfo="\n--------: newpw='"+passw+"'", isLocal=True) + + # Purge mkeys + def _purgemkeys(self): + l13 = self.kdb5_util+' purge_mkeys -f -v' #-f is forced, -v is verbose + errm = "error when purging mkeys, _purgemkeys" + self._metafunc(l13, errm) + + # Add mkey + def _addmkey(self, passw, extra="", succeed=True): + l14 = self.kdb5_util+' add_mkey '+extra + pl14 = Popen(l14.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl14.stdin.write(passw+'\n') #enter 1st time + pl14.stdin.write(passw+'\n') #re-enter + self._printout(l14+' [with password='+passw+']', pl14.stdout) + self._eval(succeed, pl14.wait(), "error when adding mkey, _addmkey", pl14.stderr) + self._writeLine( "----end of adding mkey") + + # kinit user + def _kinit(self, passw_in, usern, succeed=True): + l15 = self.clients+'kinit/kinit '+usern + pl15 = Popen(l15.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl15.stdin.write(passw_in+'\n') + pl15.stdin.close() + self._printout(l15, pl15.stdout) + self._eval(succeed, pl15.wait(), "error when kinit user, _kinit", pl15.stderr) + self._writeLine( "----end of kiniting user") + + # change password on client's side + def _kpasswd(self, oldpw, newpw, usern, succeed=True): + l16 = self.clients+'kpasswd/kpasswd '+usern + pl16 = Popen(l16.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl16.stdin.write(oldpw+'\n') + pl16.stdin.write(newpw+'\n') + pl16.stdin.write(newpw+'\n') + self._printout(l16+"\n--------: oldpw='"+oldpw+"' -> newpw='"+newpw+"'", pl16.stdout) + self._eval(succeed, pl16.wait(), "error when changing password on client's side, _kpasswd", pl16.stderr) + self._writeLine("----end of changing kpasswd") + + # klist on client's side + def _klist(self): + l17 = self.clients+'klist/klist' + errm = "error when klist, _klist" + self._metafunc(l17, errm) + + # Update principal encryption + def _updatePrincEnc(self): + l18 = self.kdb5_util+' update_princ_encryption -f -v' + errm = "error when updating principal encryption, _updatePrincEnc" + self._metafunc(l18, errm) + + # kdestroy + def _kdestroy(self): + l19 = self.clients+'kdestroy/kdestroy' + errm = "error when kdestroy, _kdestroy" + self._metafunc(l19, errm) + + # stash + def _stash(self): + l20 = self.kdb5_util+' stash' + errm="error at stash, _stash" + self._metafunc(l20, errm) + + # any shell command + def _shell(self, command, succeed=True): + l21 = command + errm="error at executing this command in _shell(): "+l21 + pl21 = Popen(l21, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l21, pl21.stdout) + self._eval(succeed, pl21.wait(), errm, pl21.stderr) + '''self._printerr(errm, pl21.stderr) Pointed out that kadmin had problems!''' + + # get_princ_records() + def _get_princ_records(self, succeed=True): + l22 = self.kadminlocal+" -q listprincs 2>/dev/null|grep -v '^Authenticating as'|fgrep '@'|sort" + errm="error at listprincs in _get_princ_records() with this command: "+l22 + pl22 = Popen(l22, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + if int(pl22.wait()) != 0: # is bad + self.printerr(errm, pl22.stderr) + if succeed: ## want good + self.nfail += 1 + self._sysexit() + else: ## want badd + self.npass += 1 + self._printig() + else: # is good + if not succeed: ## want bad + self.nfail += 1 + self._sysexit() + else: ## want good + self.npass += 1 + self._writeLine( "\nget_princ_records() executing all listprincs command: "+l22+"\n------its results:") + for princ in pl22.stdout.readlines(): + self._locGetprinc(princ.strip(), extra=True) + self._writeLine("END executing command: "+l22+"\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~") + +###################################################### + def run(self): + #############RUN################### + passw=self.pw + + self._writeHeader("START MASTER KEY MIGRATION TEST") + + # Set up database + self._writeHeader("SET UP: database") + self._killkdc("Either") #74 =1,2 + self._destroykdc("Either") #77 =3 + self._createdb(passw) #81 =4 + #line 83-86 involves ktadd kadm5.keytab, which are out dated + + # add, get, and list princs + self._writeHeader("SET UP: add/get/list princs") + self._locAddprinc(passw, 'kdc/admin') #87 =5 + self._locListprincs() #89 =6 + self._locGetprinc('K/M') #90 =7 + self._locAddprinc('test123', 'haoqili') #91 =8 + self._locGetprinc('haoqili') #92 =9 + self._locAddprinc(passw, 'haoqili/admin') #93 =10 + self._locAddprinc('foobar', 'test') #94 =11 + self._locGetprinc('test') #95 =12 + self._locListprincs() # I added =13 + myfqdn = socket.getfqdn() + #self._shell(self.parentpath+"kadmin.local -q 'addprinc -randkey host/"+myfqdn+"'") #96 + self._shell(self.kadminlocal+" -q 'addprinc -randkey host/"+myfqdn+"'") #96 =14 + + # create policies + self._writeHeader("SET UP: create policies") + #print "\n~~~~~~~~~ create policies ~~~~~~~~~~~" + self._locAddpol('8days', None, None, None, 'testpolicy')#100 =15 + self._locAddpol('20days', '8', '3', None, 'testpolicy2')#101 + self._locAddpol('90days', '2', '2', None, 'testpolicy3')#102 + self._locAddpol('90days', '2', '2', '3', 'testpolicy4')#103 + + self._locModprinc('-policy testpolicy haoqili')#105 + self._locAddprinc(passw, 'foo')#106 + self._locModprinc('-policy testpolicy3 foo')#107 =21 + + # create all princ with all fields + self._writeHeader("SET UP: create all princ with all fields") + #print "\n~~~~~~~~~ create all princ with all fields ~~~~~" + self._locAddprinc(passw, 'all') #110 =22 + self._locModprinc('-expire "2029-12-30 7pm" all') #112 + self._locModprinc('-pwexpire 12/30/2029 all') #114 + self._locGetprinc('all') #115 + self._locModprinc('-maxlife 100days all') #116 + self._locGetprinc('all') #117 + self._locModprinc('-maxrenewlife 100days all') #118 + self._locGetprinc('all') #119 + self._locModprinc('+allow_postdated +allow_forwardable all') #120 =30 + self._locModprinc('+allow_proxiable +allow_dup_skey all') #121 + self._locModprinc('+requires_preauth +allow_svr +needchange all') #122 + self._locModprinc('-policy testpolicy4 all') #123 + self._locGetprinc('all') #124 =34 + + # Testing stuff + self._writeHeader("TEST: initial mkey list") #126 + self._writeLine("===== Listing mkeys at start of test") #I add + self._listmkeys() #127 =35 + + self._writeLine( "Testing krb5kdc list_mkeys Done ==============================================") #128 + + self._writeLine("---------------\n xxxxxxxxxx \/\/\/ ERRORS (multiple) EXPECTED below xxxxxxxxxx") + self._writeLine("\nERRORS (multiple) EXPECTED below") + self._writeLine("Testing bogus use_mkey (setting only mkey to future date, using non-existent kvno, so should return error) =======") #129, 130 + self._writeLine( "-> must have a mkey currently active (setting mkey to 2 days from now), should fail and return error") #132 + self._usemkey('1', 'now+2days', False) #133-138 =36 + + self._writeLine("-> must have a mkey currently active (setting mkey to 2019 the future), should fail and return error") #140 + self._usemkey('1', '5/30/2019', False) #141 =37 + self._writeLine("-> bogus kvno and setting mkey to 2 days from now, should fail and return error") #147 + self._usemkey('2', 'now+2days', False) #148 =38 + self._writeLine("-> bogus kvno, should fail and return error") #I add + self._usemkey('2', 'now-2days', False) #I add =39 + self._writeLine( "^^^ABOVE^^ SHOULD HAVE *ALL* FAILED\n-----------------") + + self._writeLine( "Listing mkeys at end of test") #I add + self._listmkeys() #155 =40 + self._writeLine("Testing bogus use_mkey (setting only mkey to future date) Done ===========================") #156 + + + self._writeLine("\nmake sure cpw [change password] works") #158 + # this changes the password of 'test' from 'foobar' in "add, get, and list princs" above + self._locCpw('test1', 'test') #159 =41 + + self._writeHeader("TEST: bogus purge_mkeys (should be no keys purged, no error returned") + #print "\nTesting bogus purge_mkeys (should be no keys purged, no error returned) ===========================" #161 + self._purgemkeys() #162 =42 + self._writeLine("Testing bogus purge_mkeys (no error) Done ===========================") #163 + + self._writeLine( "\nadd kvno 2") #164 + self._addmkey('abcde', '-s') #165-167 =43 + self._writeLine("\nlist mkeys") + self._listmkeys() #169 =44 + + #start daemons + self._startkdc() #172 =45 46 + self._writeLine("make sure kdc is up, by kinit test") #176 + self._kinit('test1', 'test') #177 =47 + + self._writeLine("---------------\n\/\/\/ ERROR EXPECTED below. Test passwd policy.:") #180 + self._kinit(passw, 'all', succeed=False) #181 =48 + self._writeLine("^^ABOVE^^ SHOULD HAVE FAILED\n-----------------") + + #change passwd on client's side + self._kpasswd(passw, 'Test123.', 'all')#184-188 =49 + + self._kinit('Test123.', 'all') #189 =50 + self._klist() #190 =51 + + self._writeHeader("TEST: password history for principal 'all', new passwords must not be a previous password") #191 + self._kpasswd('Test123.', 'Foobar2!', 'all') #192-195 =52 + self._writeLine("--------------\n\/\/\/ ERROR EXPECTED below") #197 + self._kpasswd('Foobar2!', passw, 'all', succeed=False) #199-202 =53 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n----------") + + # this shouldn't change the mkvno for any princs (should be 1) #206 + #self._updatePrincEnc() #207 + # princs should still be protected by mkvno 1 #208 + self._writeLine("@@@@@@@@ Wait for other people to fix bug in code 6507 update_princ_encryption to use mkey instead of latest mkey @@@@@@@@@@@@@\n") + self._locGetprincFind('test', 'MKey: vno 1') #209 =54 + + self._purgemkeys() #210 =55 + self._listmkeys() #211 =56 + self._usemkey('2', 'now-1day') #213 =57 + self._listmkeys() #214 =58 + + self._writeLine("-----------\n\/\/\/ ERROR EXPECTED below") #216 + self._kpasswd('Foobar2!', passw, 'all', succeed=False) #217-221 =59 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n--------") + + self._kpasswd('Foobar2!', 'Barfoo3.', 'all') #224-228 =60 + self._kinit('Barfoo3.', 'all') #229 + self._klist() #230 =62 + + self._writeLine("-------------\n\/\/\/ ERROR EXPECTED below") #231 + self._kpasswd('Barfoo3.', 'Foobar2!', 'all',succeed=False) #233-235 =63 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n---------") + + self._writeLine("\nTest's key should be protected by mkvno 2" ) #239 + self._locCpw('foo', 'test') #240 =64 + self._locGetprincFind('test', 'MKey: vno 2') #241 =65 + self._kdestroy() #242 =66 + + self._writeHeader("TEST: krb5kdc refetch of mkey")#243 + self._kinit('foo', 'test') #244 =67 + self._klist() #245 =68 + self._writeLine("END. Testing krb5kdc refetch of mkey list Done ==============================================\n") #246 + + self._updatePrincEnc() #247 =69 + self._get_princ_records() #248 =70 -83 + self._kdestroy() #249 =84 + self._kinit('foo', 'test') #250 =85 + self._purgemkeys() #252 =86 + + self._stash() #254 =87 + self._shell(self.clients+'klist/klist' +" -ekt "+self.sandir+"/keyStashFile") #255 =88 + + self._locGetprinc('K/M') #256 =89 + self._purgemkeys() #257 =90 + self._locGetprinc('K/M') #258 + self._listmkeys() #259 =92 + self._kdestroy() #260 + self._kinit('foo', 'test') #261 + self._klist() #262 =95 + + self._writeLine("\n Adding in Master Key Number 3") + self._listmkeys() #265 =96 + self._addmkey('abcde') #266-268 + self._listmkeys() #270 =98 + self._locCpw('foo', 'all') #271 + self._locGetprinc('all') #272 =100 + self._usemkey('3', 'now') #273 + self._listmkeys() #274 =102 + self._locCpw('99acefghI0!', 'all') #275 + self._locGetprinc('all') #276 =104 + self._kdestroy() #277 + self._kinit('foo', 'test') #279 =106 + self._klist() #280 + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'listprincs'") #281 =108 + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'getprinc test'") #282 =109 + + self._writeHeader("TEST: add_mkey with aes128 enctype") #283 + self._addmkey('abcde', '-e aes128-cts-hmac-sha1-96') #284-287 =110 + self._listmkeys() #288 =111 + + self._writeLine( "END. Testing add_mkey with aes128 enctype done ==============================================")#289 + + self._writeHeader("TEST: krb5kdc refetch of mkey list") + self._usemkey('4', 'now') #290 =112 + self._listmkeys() #291 =113 + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'cpw -pw abcde test'") #292 =114 + self._shell(self.kadmin +" -p haoqili/admin -w "+passw+" -q 'getprinc test'") #293 + self._kdestroy() #294 =116 + + self._writeLine("\nTesting krb5kdc refetch of mkey list =================================================") #295 + self._kinit('abcde', 'test') #296 =117 + #'self._klist() #297 =118 + self._writeLine("Testing krb5kdc refetch of mkey list Done :) =================================================\n") #298 + + self._killkdc() #300 =119, 120 + self._startkdc() #301 =121 122 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'cpw -pw foo test'") #304 =123 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc test'") #305 =124 + self._kdestroy() #307 =125 + + self._writeLine("\nTesting krb5kdc refetch of mkey list =================================================") #308 + self._kinit('foo', 'test') #309 =126 + self._klist() #310 =127 + self._writeLine("Testing krb5kdc refetch of mkey list Done =================================================\n") #311 + + self._updatePrincEnc() #313 =128 + self._locGetprinc('K/M') #314 + self._locGetprinc('all') #315 =130 + self._locGetprinc('haoqili') #316 + self._kdestroy() #317 =132 + self._kinit('foo', 'test') #318 + self._stash() #319 =134 + self._shell(self.clients+'klist/klist' + " -ekt "+self.sandir+"/keyStashFile") #320 + self._locGetprinc('K/M') #321 =136 + self._purgemkeys() #322 + self._locGetprinc('K/M') #323 =138 + self._locGetprinc('all') #324 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc test'") #325 =140 + self._listmkeys() #326 + self._kdestroy() #327 =142 + self._kinit('foo', 'test') #328 + self._klist() #329 =144 + + self._get_princ_records() #330 =145-158 + + self._writeHeader("TEST: add_meky with DES-crc enctype") + #print "\nTesting add_mkey with DES-crc enctype ==============================================" #331 + self._addmkey('abcde', '-e des-cbc-crc') #332-335 =159 + self._listmkeys() #336 =160 + self._writeLine( "END. Testing add_mkey with DES-crc enctype Done ==============================================") #337 + self._addmkey('abcde') #338-341 =161 + self._listmkeys() #342 =162 + self._writeLine( "current time: "+strftime("%Y-%m-%d %H:%M:%S") ) #343 + + self._usemkey('5', 'now-1day') #344 =163 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#345 + self._listmkeys() #346 =164 + self._usemkey('5', 'now') #347 =165 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#348 + self._listmkeys() #349 =166 + self._usemkey('5', 'now+3days') #350 =167 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#351 + self._listmkeys() #352 =168 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#353 + self._usemkey('5', 'now+5sec') #354 =169 + self._listmkeys() #355 =170 + time.sleep(5) #356 + self._listmkeys() #357 =171 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#358 + self._usemkey('4', 'now+5sec') #359 =172 + self._listmkeys() #360 =173 + time.sleep(5) #361 + self._listmkeys() #362 =174 + self._usemkey('5', 'now+3days') #363 =175 + + self._writeLine("------------\n\/\/\/ ERROR EXPECTED below" )#364 + self._writeLine("should fail, because there must be one mkey currently active") #365 + self._usemkey('4', 'now+2days', False) #366 =176 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n---------------") + + self._listmkeys() #373 =177 + self._usemkey('4', '1/30/2009') #375 =178 + + self._writeHeader("TEST: purge_mkeys (removing mkey 5)") + #print "\nTesting purge_mkeys (removing mkey 5) ==============================================" #378 + self._purgemkeys() #379 =179 + self._stash() #380 =180 + self._shell(self.clients+'klist/klist' +" -ekt "+self.sandir+"/keyStashFile") #381 =181 + self._listmkeys() #382 =182 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc K/M'") #383 =183 + self._writeLine("Testing purge_mkeys Done ==============================================") #384 + self._writeHeader("MASTER KEY MIGRATION TEST DONE. please consult 'outfile' in your sandbox for more info. The sandbox is at: %s" % self.sandir) + # I added + self._sysexit(finished=True) + +#################################################### +#################################################### + +class Launcher: + def __init__(self, path, sandP): + # srcdir, self._buildDir = InPath + self._buildDir = path + # self._confDir = InPath/tests/mk_migr/input_conf + self._confDir = '%s/tests/mk_migr/db2_backend/input_conf' % self._buildDir + + # setting up sand box + if sandP != "": + self._sandboxDir = sandP + else: + self._sandboxDir = '%s/tests/mk_migr/db2_backend/sandbox' % self._buildDir + + self._vars = {'srcdir': self._buildDir, + 'sandboxdir': self._sandboxDir, + 'localFQDN':socket.getfqdn()} + + def _prepSandbox(self): + sandir = self._sandboxDir + if os.path.exists(sandir): + shutil.rmtree(sandir) + print "------about to make sandbox, with the path of:" + print sandir + os.makedirs(sandir, 0777) + print "------sandbox made" + return sandir + + def _createFileFromTemplate(self, outpath, template, vars): + fin = open(template, 'r') + result = fin.read() % vars + fin.close() + fout = open(outpath, 'w') + fout.write(result) + fout.close() + + ####### Launcher RUN ################ + def runLauncher(self): + # create sandbox file directory if it does not exit + sandir = self._prepSandbox() + '''print os.environ + ''' + #save the initial 3 things setup + orig_libpath = os.getenv('LD_LIBRARY_PATH') + orig_krbconf = os.getenv('KRB5_CONFIG') + orig_kdcprof = os.getenv('KRB5_KDC_PROFILE') + + # change the 3 things + os.environ["LD_LIBRARY_PATH"] = '%s/lib' % self._buildDir + #os.environ["SRCDIR"] = '%s' % self._buildDir + + str1 = '%s/krb5.conf' % self._sandboxDir + os.environ["KRB5_CONFIG"] = str1 + + str2 = '%s/kdc.conf' % self._sandboxDir + os.environ["KRB5_KDC_PROFILE"] = str2 + + str3 = '%s/kadm5.acl' % self._sandboxDir + + # Create adequate to the environment config files + self._createFileFromTemplate('%s' % str1, '%s/%s' % (self._confDir, 'krb5_template_db2.conf'), self._vars) + self._createFileFromTemplate('%s' % str2, '%s/%s' % (self._confDir, 'kdc_template_db2.conf'), self._vars) + self._createFileFromTemplate('%s' % str3, '%s/%s' % (self._confDir, 'kadm5_template_db2.acl'), self._vars) + + return sandir + +#################################################### +#################################################### +def makeBool(aStr): + if aStr == "True" or aStr == "T": + return True + if aStr == "False" or aStr == "F": + return False + else: + print "did NOT execute due to invalid True False argument. Please enter either 'True', 'T', 'False', or 'F'" + sys.exit() + +# # # # # # # # # # # # # # # # # # # # # # # # # + +def processInputs(parser): + # get inputs + (options, args) = parser.parse_args() + + verbose = makeBool(options.opVerbose) + pw = options.opPassword + + kdcPath = options.opKdcPath #1 + kdmdPath = options.opKdmdPath #2 + kdbPath = options.opKdbPath #3 + kdmlPath = options.opKdmlPath #4 + kdmPath = options.opKdmPath #5 + cltPath = options.opCltPath #6 + + sandPath = options.opSandbox + + ########### Launch ############### + + print "\n############ Start Launcher #############" + src_path=os.environ["PWD"] + print "SOURCE PATH ==>" , src_path + + myLaunch = Launcher(src_path, sandPath) + sandir = myLaunch.runLauncher() + + test = MasterKeyMigrationTest(verbose, pw, kdcPath, kdmdPath, kdbPath, kdmlPath, kdmPath, cltPath, sandir) + print "########## Finished Launcher ############\n" + + return test +# # # # # # # # # # # # # # # # # # # # # # # # # + +def makeParser(): + usage = "\n\t%prog [-v][-p][-c][-d][-b][-l][-t][-s]" + description = "Description:\n\tTests for the master key migration commands." + parser = OptionParser(usage=usage, description=description) + + parser.add_option("-v", "--verbose", type="string", dest="opVerbose", +default="True", help="'True' or 'False'. Switch on for details of command lines and outputs. Default is 'True'") + + parser.add_option("-p", "--password", type="string", dest="opPassword", default="test123", help="master password for many of the passwords in the test. Default is 'test123'") + + ## Default Paths + dSrcPath = src_path=os.environ["PWD"] + dKdcPath = '%s/kdc/krb5kdc' % dSrcPath #1 + dKdmdPath = '%s/kadmin/server/kadmind' % dSrcPath #2 + dKdbPath = '%s/kadmin/dbutil/kdb5_util' % dSrcPath #3 + dKdmlPath = '%s/kadmin/cli/kadmin.local' % dSrcPath #4 + dKdmPath = '%s/kadmin/cli/kadmin' % dSrcPath #5 + dCltPath = '%s/clients' % dSrcPath #6 + + parser.add_option("-c", "--krb5kdcpath", +type="string", dest="opKdcPath", +default=dKdcPath, help="set krb5kdc path, default="+dKdcPath) #1 + + parser.add_option("-d", "--kadmindpath", +type="string", dest="opKdmdPath", +default=dKdmdPath, help="set kadmind path, default="+dKdmdPath) #2 + + parser.add_option("-b", "--kdb5_utilpath", +type="string", dest="opKdbPath", +default=dKdbPath, help="set kdb5_util path, default="+dKdbPath) #3 + + parser.add_option("-l", "--kadminlocalpath", +type="string", dest="opKdmlPath", +default=dKdmlPath, help="set kadmin.local path, default="+dKdmlPath) #4 + + parser.add_option("-n", "--kadminpath", +type="string", dest="opKdmPath", +default=dKdmPath, help="set kadmin path, default="+dKdmPath) #5 + + parser.add_option("-t", "--clientspath", +type="string", dest="opCltPath", +default=dCltPath, help="set clients path, default="+dCltPath) #6 + + # set up / initializing stuff for the sandbox + parser.add_option("-s", "--sandbox", +type="string", dest="opSandbox", +default="", +help="path for the sandbox. Default is '%s/tests/mk_migr/db2_backend/sandbox' % "+dSrcPath) + + return parser + +#################################################### +if __name__ == '__main__': + parser = makeParser() + test = processInputs(parser) + result = test.run() diff --git a/src/tests/mk_migr/ldap_backend/README_for_mkmldap b/src/tests/mk_migr/ldap_backend/README_for_mkmldap new file mode 100644 index 0000000000..1ab3cdecfb --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/README_for_mkmldap @@ -0,0 +1,77 @@ +############################################################################## +###################### WARNING: DOES NOT WORK YET ############################ +############################################################################## + +ABOUT: +A translation of Will Fiveash's "mit_db2_mkey_migrate_testB" ksh code into Python with ldap backend. With minor fixes and changes. Written by HaoQi Li. + +DEFAULT SETTINGS: +Options Name Default Setting + -h Help + -v Verbose: True + -p Testing pw: test123 + -s Sandbox loc: src/tests/kdc_realm2/sandbox + -c Krb5kdc: src/kdc/krb5kdc + -d Kadmind: src/kadmin/server/kadmind + -b Kdb5_util: src/kadmin/dbutil/kdb5_util + -a Kdb5_ldap_util: src/plugins/kdb/ldap/ldap_util/kdb5_ldap_util + -l Kadmin.local: src/kadmin/cli/kadmin.local + -n Kadmin: src/kadmin/cli/kadmin + -t Client paths: src/clients + +INPUTS: +* src/tests/mk_migr/ldap_backend/input_conf/kdc_template_ldap.conf +* src/tests/mk_migr/ldap_backend/input_conf/krb5_template_ldap.conf +* src/tests/mk_migr/ldap_backend/input_conf/kadm5_template_ldap.acl +* src/tests/mk_migr/ldap_backend/input_conf/debconfile + +OUTPUTS: +* sandbox that contains customized outfile with all commands and their outputs, kdc.conf, krb5.conf, kadm6.acl, and others. +* Statistics on screen of number of commands passed and failed (if not interrupted by fatal failures). + +EXAMPLES: +- MUST RUN from trunk/src. +* python tests/mk_migr/ldap_backend/ldap7.py + Using all Default Settings. +* python tests/mk_migr/ldap_backend/ldap7.py -s /tmp/mySandbox + Sandbox now can be found in /tmp/mySandbox. + +REFERENCE: +http://k5wiki.kerberos.org/wiki/LDAP_on_Kerberos +and http://k5wiki.kerberos.org/wiki/User_talk:Haoqili + +NOTES: +* "haoqili" is a name that can be changed. +* "kdb5_util stash" is equivalent to "-s" in "kdb5_ldap_util create -s" +* 2019 and 2029 are future dates that should best be written not as fixed. Such as now+10years. + +FAILURES: + +* failure in kpasswd all, ERROR:"password history principal key version mismatch while trying to change password." This is caused by "-history 3" in testpolicy4 + +* The beginning of a series of failures starts from: the "kdb5_util list_mkeys" fails after "kdb5_util add_mkey -e aes128-cts-hmac-sha1-96 [with password=abcde]" +ERROR:------------------------------------- +kdb5_util: Unable to decrypt latest master key with the provided master key +while getting master key list +kdb5_util: Warning: proceeding without master key list +kdb5_util: master keylist not initialized +can't decrypt the latest master key +-------------------------------------------- +Convo with Tom: +T: so you didn't activate the new mkey? +H: correct i just added it +T: the message looks familiar. does list_mkeys work before you do that add_mkey? +H: yes it does +T: Will might have mentioned some problems with the LDAP backend and the master key migration stuff. +T: how up-to-date is your source tree? Will says he remembers fixing this. +H: i'm at revision 22523 +T: hm, i think that should be recent enough. +T: do you have any enctype settings in your config files? +H: in krb5.conf +[libdefaults] +default_realm = EXAMPLE.ORG +default_tkt_enctypes = des3-hmac-sha1 aes128-cts +default_tgs_enctypes = des3-hmac-sha1 aes128-cts +T: anything for supported_enctypes or master_key_type? +H: no + diff --git a/src/tests/mk_migr/ldap_backend/input_conf/debconfile b/src/tests/mk_migr/ldap_backend/input_conf/debconfile new file mode 100644 index 0000000000..6866e93558 --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/input_conf/debconfile @@ -0,0 +1,9 @@ +slapd slapd/no_configuration boolean false +slapd slapd/domain string example.org +slapd shared/organization string My Organization +slapd slapd/backend select HDB +slapd slapd/purge_database boolean true +slapd slapd/move_old_database boolean true +slapd slapd/password1 password a +slapd slapd/password2 password a +slapd slapd/allow_ldap_v2 boolean false diff --git a/src/tests/mk_migr/ldap_backend/input_conf/kadm5_template_ldap.acl b/src/tests/mk_migr/ldap_backend/input_conf/kadm5_template_ldap.acl new file mode 100644 index 0000000000..719677a865 --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/input_conf/kadm5_template_ldap.acl @@ -0,0 +1 @@ +*/admin * diff --git a/src/tests/mk_migr/ldap_backend/input_conf/kdc_template_ldap.conf b/src/tests/mk_migr/ldap_backend/input_conf/kdc_template_ldap.conf new file mode 100644 index 0000000000..94a82a7528 --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/input_conf/kdc_template_ldap.conf @@ -0,0 +1,17 @@ +[kdcdefaults] + kdc_ports = 8888 + +[realms] + EXAMPLE.ORG = { + database_name = %(sandir)s/krb5kdc/principal + acl_file = %(sandir)s/kadm5.acl + key_stash_file = %(sandir)s/krb5kdc/.k5.EXAMPLE.ORG + admin_keytab = FILE:%(sandir)s/krb5kdc/kadm5.keytab + kdc_ports = 8888 + kpasswd_port = 8887 + kadmind_port = 8886 + max_life = 10h 0m 0s + max_renewable_life = 7d 0h 0m 0s + } +[logging] + kdc = FILE:/tmp/myrealKDC.log diff --git a/src/tests/mk_migr/ldap_backend/input_conf/krb5_template_ldap.conf b/src/tests/mk_migr/ldap_backend/input_conf/krb5_template_ldap.conf new file mode 100644 index 0000000000..5805f60ba8 --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/input_conf/krb5_template_ldap.conf @@ -0,0 +1,33 @@ +[libdefaults] + default_realm = EXAMPLE.ORG + default_tkt_enctypes = des3-hmac-sha1 aes128-cts + default_tgs_enctypes = des3-hmac-sha1 aes128-cts + +[realms] + EXAMPLE.ORG = { + admin_server = %(localFQDN)s:8886 + kpasswd_server = %(localFQDN)s:8887 + #default_domain = EXAMPLE.ORG + kdc = %(localFQDN)s:8888 + database_module = LDAP + } + +[dbdefaults] + ldap_kerberos_container_dn = "cn=krbContainer,dc=example,dc=org" + +[dbmodules] + LDAP = { + db_library = kldap + ldap_kerberos_container_dn = "cn=krbContainer,dc=example,dc=org" + ldap_kdc_dn = cn=admin,dc=example,dc=org + ldap_kadmind_dn = cn=admin,dc=example,dc=org + ldap_service_password_file = %(sandir)s/krb5kdc/admin.stash + ldap_servers = ldapi:/// + } +[domain_realm] + +[logging] + kdc = FILE:/tmp/kdc_fromkrb.log + default = FILE:/tmp/krb5.log + admin_server = FILE:/tmp/admin.log + diff --git a/src/tests/mk_migr/ldap_backend/mkmldap.py b/src/tests/mk_migr/ldap_backend/mkmldap.py new file mode 100644 index 0000000000..ae2b83a200 --- /dev/null +++ b/src/tests/mk_migr/ldap_backend/mkmldap.py @@ -0,0 +1,897 @@ +############################################################################## +###################### WARNING: DOES NOT WORK YET ############################ +############################################################################## + +import os, sys, shutil, socket, time, string +from subprocess import Popen, PIPE +from optparse import OptionParser +from time import strftime + +class LDAPbackendSetup: + def __init__(self, verbose_in, pw_in, kdcPath_in, kdmdPath_in, kdbPath_in, ldapPath_in, kdmlPath_in, kdmPath_in, cltPath_in, sandir_in, confdir_in): + self.npass = 0 + + self.nfail = 0 + + self.verbose = verbose_in + self.pw = pw_in + + self.krb5kdc = kdcPath_in #1 krb5kdc + self.kadmind = kdmdPath_in #2 kadmind + self.kdb5_util = kdbPath_in #3a kdb5_util + self.kdb5_ldap_util = ldapPath_in #3b kdb5_ldap_util + self.kadminlocal = kdmlPath_in #4 kadmin.local + self.kadmin = kdmPath_in #5 kadmin + self.clients = cltPath_in+"/" #6 clients + + self.sandir = sandir_in + self.confdir = confdir_in + + ########## SET UP Write Output File ##### + print "outfile path" + print self.sandir + print self.sandir+"/outfile" + + self.outfile = open(self.sandir+"/outfile", 'w') + + #''print os.environ' + + def _writeLine(self, astr, prt=False): + self.outfile.write(astr.strip()+"\n") + if prt: + print astr.strip() + + def _writeHeader(self, astr, prt=True): + self.outfile.write("\n========== "+astr.strip()+" ==========\n") + if prt: + print "========== "+astr.strip()+" ==========" + + def _sysexit(self, fatal=False, finished=False): + self._writeLine("++++++++++++++++++++++++++++++", True) + if fatal: + self._writeLine("++++ Test did NOT finish +++++", True) + self._writeLine("++++ FATAL FAILURE! Stopped ++", True) + self._writeLine("++++ See sandbox/outfile +++++", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + sys.exit() + elif not finished: + self._writeLine("++++ Test did NOT finish +++++", True) + self._writeLine("++++ FAIL Detected! keep going", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + else: #finished + self._writeLine("++++ MKM Test Finished +++++++", True) + self._writeLine("++++++++++++++++++++++++++++++", True) + self._writeLine("++++ Commands Passed: %s +++++" % self.npass, True) + self._writeLine("++++ Commands Failed: %s +++++" % self.nfail, True) + sys.exit() + + def _printig(self): + self._writeLine("~.~.~Error should be ignored~.~.~.~") + + def _printerr(self, errm, stderr): + self._writeLine("#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#@#") + self._writeLine("-XX-FAILED: "+errm+". See stderr below:") + [self._writeLine(line) for line in stderr.readlines() ] + + def _printout(self, cmd, pstdout): + if self.verbose: + self._writeLine("#######################################") + #self._writeLine("---------------------------------------") + self._writeLine("-command: "+cmd) + self._writeLine("-----out: ") + [self._writeLine(line) for line in pstdout.readlines()] + + def _eval(self, succeed, pwait, errm, pstderr, fatal=False, msg2="", finished=False): + if int(pwait) != 0: # is bad + self._printerr(errm, pstderr) + if succeed==True: ## want good + self.nfail += 1 + self._sysexit(fatal, finished) + else: ## want bad + self.npass += 1 + self._printig() + else: # is good + if not succeed: ## want bad + if msg2 != "": + self._writeLine(msg2, True) + self.nfail += 1 + self._sysexit(fatal, finished) + else: ## want good + self.npass += 1 + + def _metafunc(self, command, errmsg, moreinfo="", isLocal=False, succeed=True, fatal=False): + l = command + if isLocal: + pl = Popen(l.split(None,2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + else: + pl = Popen(l.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l+moreinfo, pl.stdout) + + self._eval(succeed, pl.wait(), errmsg, pl.stderr, fatal) + + ########################################### + + # Start the KDC daemons + def _startkdc(self): + self._writeLine("\nstarting kdc daemons ...") + l0 = self.krb5kdc + errm = "error at starting krb5kdc" + self._metafunc(l0, errm) + # below has been changed + + #starting kadmind + l0b = self.kadmind + ' -W -nofork' #the W is for during off strong random numbers + errm = "error at starting kadmind, maybe it's already started" + pl0b = Popen(l0b.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._writeLine( "kadmind -nofork") + started = False + while time.clock() < 3: + l = pl0b.stderr.readline() + if l.find("starting") > -1: + self._writeLine( l.strip()) + self.npass += 1 + started = True + break + else: + self.nfail += 1 + self._printerr("kadmind not starting, check to see if there are any previous kadmind running with cmd: 'ps -ef | grep kadmind' and then do 'sudo kill -9 [# on the left]'", pl0b.stderr) + self._sysexit(fatal=True) + if not started: + self.nfail += 1 + self._sysexit() + self._writeLine("end starting kdc daemons") + + # Kill the KDC daemons in case they are running + def _killkdc(self, suc=True): + l1 = 'pkill -9 -x krb5kdc' + errm = "no krb5kdc killed" + self._metafunc(l1, errm, succeed=suc) + l2 = 'pkill -9 -x kadmind' + errm = "no kadmind killed" + self._metafunc(l2, errm, succeed=suc) + + # Destroys current database + def _destroykdc(self, suc=True): + l3 = self.kdb5_util+' destroy -f' #forced + errm = "no kdb database destroyed" + self._metafunc(l3, errm, succeed=suc) + + ''' Destroys current database + I don't use this because 1. I don't know the specific kdc's to destroy, 2. the debconf setting up of slapd has destroyed old databases already + def _destroykdc_ldap(self, suc=True): + l3 = self.kdb5_ldap_util+' destroy -f' #forced + errm = "no kdb database destroyed" + self._metafunc(l3, errm, succeed=suc) + ''' + + # Create a new database with a new master key + def _createdb(self, pw): + l4 = self.kdb5_util+' -P '+pw+' create -s -W' #added W for svn version 22435 to avoid reading strong random numbers + errm = "error when creating new database, _createdb()" + self._metafunc(l4, errm, fatal=True) + + # Addprinc + def _locAddprinc(self, passw, usern): + l5 = self.kadminlocal+' -q addprinc -pw '+passw+' '+usern + errm = "error when adding princ, _locAddprinc" + self._metafunc(l5, errm, isLocal=True) + + # List princs + def _locListprincs(self): + l6 = self.kadminlocal+' -q listprincs' + errm = "error when listing princs, _locListprincs" + self._metafunc(l6, errm, isLocal=True) + + # Get princs + def _locGetprinc(self, usern, extra=False, succeed=True): + l7 = self.kadminlocal+' -q getprinc '+usern + errm="error when getting princ, _locGetprinc" + + pl7 = Popen(l7.split(None,2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + if not extra: + self._printout(l7, pl7.stdout) + else: + if self.verbose: + self._writeLine("-command: "+l7) + self._writeLine("-----out: ") + for line in pl7.stdout.readlines(): + if line.startswith("Princ") or line.startswith("MKey"): + self._writeLine(line) + self._eval(succeed, pl7.wait(), errm, pl7.stderr) + + # Get princs and finds something in the output + def _locGetprincFind(self, usern, findstr, succeed=True): + l7b = self.kadminlocal+' -q getprinc ' +usern + errm="error when getting princs, _locGetprinc, (regular output of getprincs is not printed here), will NOT continue to find string="+findstr + pl7b = Popen(l7b.split(None, 2), stdin=PIPE, stdout=PIPE, stderr=PIPE) + if self.verbose: + self._writeLine("-command: "+l7b) + if int(pl7b.wait()) != 0: # is bad + self._printerr(errm, pl7b.stderr) + if succeed: ## want good + self.nfail += 1 + self._sysexit() + else: ## want bad + self.npass += 1 + self._printig() + else: # is good + if self.verbose: + self._writeLine( "-----out: ") + boofound = False + for outl in pl7b.stdout.readlines(): + self._writeLine(outl) + if string.find(outl, findstr) > -1: + boofound = True + if boofound: + self._writeLine("----FOUND: "+findstr) + else: + self._writeLine("----NOT FOUND: "+findstr) + if not succeed: ## want bad + self.nfail += 1 + self._sysexit() + else: ## want good + self.npass += 1 + + # Add policy + def _locAddpol(self, maxtime, minlength, minclasses, history, policyname): + rest = "" + if maxtime != None: + rest += '-maxlife '+maxtime+' ' + if minlength != None: + rest += '-minlength '+minlength+' ' + if minclasses != None: + rest += '-minclasses '+minclasses+' ' + if history != None: + rest += '-history '+history+' ' + l8 = self.kadminlocal+' -q add_policy '+rest+policyname + errm = "error when adding policy, _locAddpol" + self._metafunc(l8, errm, isLocal=True) + + # Get pol + def _locGetpol(self, poln): + l8b = self.kadminlocal+' -q getpol '+poln + errm="error when getting pol, _locGetpol" + self._metafunc(l8b, errm, isLocal=True) + + # Modify Principal + def _locModprinc(self, rest): + l9 = self.kadminlocal+' -q modprinc '+rest + errm = "error when modifing principal, _locModprinc" + self._metafunc(l9, errm, isLocal=True) + + # List mkeys + def _listmkeys(self): + l10 = self.kdb5_util+' list_mkeys' + errm = "error when listing mkeys, _listmkeys" + self._metafunc(l10, errm) + + # Use mkeys + def _usemkey(self, kvno, time, succeed=True): + l11 = self.kdb5_util+' use_mkey '+kvno+' '+time + pl11 = Popen(l11.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l11, pl11.stdout) + self._eval(succeed, pl11.wait(), "error when using mkeys, _usemkey", pl11.stderr, msg2="-XX-ERROR: "+l11+" should have failed.") + + + # Change password (cpw) + def _locCpw(self, passw, usern): + l12 = self.kadminlocal+' -q cpw -pw '+passw+' '+usern + errm = "error when changing password, _locCpw" + self._metafunc(l12, errm, moreinfo="\n--------: newpw='"+passw+"'", isLocal=True) + + # Purge mkeys + def _purgemkeys(self): + l13 = self.kdb5_util+' purge_mkeys -f -v' #-f is forced, -v is verbose + errm = "error when purging mkeys, _purgemkeys" + self._metafunc(l13, errm) + + # Add mkey + def _addmkey(self, passw, extra="", succeed=True): + l14 = self.kdb5_util+' add_mkey '+extra + pl14 = Popen(l14.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl14.stdin.write(passw+'\n') #enter 1st time + pl14.stdin.write(passw+'\n') #re-enter + self._printout(l14+' [with password='+passw+']', pl14.stdout) + self._eval(succeed, pl14.wait(), "error when adding mkey, _addmkey", pl14.stderr) + self._writeLine( "----end of adding mkey") + + # kinit user + def _kinit(self, passw_in, usern, succeed=True): + l15 = self.clients+'kinit/kinit '+usern + pl15 = Popen(l15.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl15.stdin.write(passw_in+'\n') + pl15.stdin.close() + self._printout(l15, pl15.stdout) + self._eval(succeed, pl15.wait(), "error when kinit user, _kinit", pl15.stderr) + self._writeLine( "----end of kiniting user") + + # change password on client's side + def _kpasswd(self, oldpw, newpw, usern, succeed=True): + l16 = self.clients+'kpasswd/kpasswd '+usern + pl16 = Popen(l16.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) + pl16.stdin.write(oldpw+'\n') + pl16.stdin.write(newpw+'\n') + pl16.stdin.write(newpw+'\n') + self._printout(l16+"\n--------: oldpw='"+oldpw+"' -> newpw='"+newpw+"'", pl16.stdout) + self._eval(succeed, pl16.wait(), "error when changing password on client's side, _kpasswd", pl16.stderr) + self._writeLine("----end of changing kpasswd") + + # klist on client's side + def _klist(self): + l17 = self.clients+'klist/klist' + errm = "error when klist, _klist" + self._metafunc(l17, errm) + + # Update principal encryption + def _updatePrincEnc(self): + l18 = self.kdb5_util+' update_princ_encryption -f -v' + errm = "error when updating principal encryption, _updatePrincEnc" + self._metafunc(l18, errm) + + # kdestroy + def _kdestroy(self): + l19 = self.clients+'kdestroy/kdestroy' + errm = "error when kdestroy, _kdestroy" + self._metafunc(l19, errm) + + # stash + def _stash(self): + l20 = self.kdb5_util+' stash' + errm="error at stash, _stash" + self._metafunc(l20, errm) + + # any shell command + def _shell(self, command, succeed=True): + l21 = command + errm="error at executing this command in _shell(): "+l21 + pl21 = Popen(l21, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + self._printout(l21, pl21.stdout) + self._eval(succeed, pl21.wait(), errm, pl21.stderr) + #'self._printerr(errm, pl21.stderr) Pointed out that kadmin had problems!' + + + def _shelltest(self, command, succeed=True): + l21 = command + errm="error at executing this command in _shell(): "+l21 + pl21 = Popen(l21, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + first = pl21.communicate('a\na')[0] + print "first:" + print first + print "end first" + + #self._printout(l21, pl21.stdout) self._printout(l21, first) + self._eval(succeed, pl21.wait(), errm, pl21.stderr) + #self._printerr(errm, pl21.stderr) #Pointed out that kadmin had problems!' + + + # get_princ_records() + def _get_princ_records(self, succeed=True): + l22 = self.kadminlocal+" -q listprincs 2>/dev/null|grep -v '^Authenticating as'|fgrep '@'|sort" + errm="error at listprincs in _get_princ_records() with this command: "+l22 + pl22 = Popen(l22, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) + if int(pl22.wait()) != 0: # is bad + self.printerr(errm, pl22.stderr) + if succeed: ## want good + self.nfail += 1 + self._sysexit() + else: ## want badd + self.npass += 1 + self._printig() + else: # is good + if not succeed: ## want bad + self.nfail += 1 + self._sysexit() + else: ## want good + self.npass += 1 + self._writeLine( "\nget_princ_records() executing all listprincs command: "+l22+"\n------its results:") + for princ in pl22.stdout.readlines(): + self._locGetprinc(princ.strip(), extra=True) + self._writeLine("END executing command: "+l22+"\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~") + +###################################################### + def run(self): + #############RUN################### + passw=self.pw + + self._writeHeader("START MASTER KEY MIGRATION TEST") + + # Set up database + self._writeHeader("SET UP: database") + self._killkdc("Either") #74 =1,2 + #self._destroykdc("Either") #77 =3 + #self._destroykdc_ldap("Either") #77 =3 + + self._shell('sudo cat '+self.confdir+'/debconfile') + self._shell('sudo debconf-set-selections '+self.confdir+'/debconfile') + self._shell('sudo dpkg-reconfigure --frontend=noninteractive slapd') + self._shell('sudo ldapadd -x -D cn=admin,cn=config -w a -f /tmp/ldif_output/cn\=config/cn\=schema/cn\=\{6\}kerberos.ldif -H ldapi:///') + self._shell('kdb5_ldap_util -D cn=admin,dc=example,dc=org -w a -H ldapi:/// create -P a -s') #self._createdb(passw) #81 =4 + self._shelltest('kdb5_ldap_util -D cn=admin,dc=example,dc=org -w a -H ldapi:/// stashsrvpw cn=admin,dc=example,dc=org') + #self._shell('krb5kdc') ## MUST KILL krb5kdc before first! + self._writeHeader("+++++ START +++++") + + #line 83-86 involves ktadd kadm5.keytab, which are out dated + + # add, get, and list princs + self._writeHeader("SET UP: add/get/list princs") + self._locAddprinc(passw, 'kdc/admin') #87 =5 + self._locListprincs() #89 =6 + self._locGetprinc('K/M') #90 =7 + self._locAddprinc('test123', 'haoqili') #91 =8 + self._locGetprinc('haoqili') #92 =9 + self._locAddprinc(passw, 'haoqili/admin') #93 =10 + self._locAddprinc('foobar', 'test') #94 =11 + self._locGetprinc('test') #95 =12 + self._locListprincs() # I added =13 + myfqdn = socket.getfqdn() + #self._shell(self.parentpath+"kadmin.local -q 'addprinc -randkey host/"+myfqdn+"'") #96 + self._shell(self.kadminlocal+" -q 'addprinc -randkey host/"+myfqdn+"'") #96 =14 + + # create policies + self._writeHeader("SET UP: create policies") + + #print "\n~~~~~~~~~ create policies ~~~~~~~~~~~" + self._locAddpol('8days', None, None, None, 'testpolicy')#100 =15 + self._locAddpol('20days', '8', '3', None, 'testpolicy2')#101 + self._locAddpol('90days', '2', '2', None, 'testpolicy3')#102 + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #!!!!!!!!!Changed to avoid problem in 'kpasswd all'!!!!!!!!!!!!!!!!!!!! + #self._locAddpol('90days', '2', '2', '3', 'testpolicy4')#103 + self._locAddpol('90days', '2', '2', None, 'testpolicy4')#103 + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + self._locModprinc('-policy testpolicy haoqili')#105 + self._locAddprinc(passw, 'foo')#106 + self._locModprinc('-policy testpolicy3 foo')#107 =21 + + # create all princ with all fields + self._writeHeader("SET UP: create all princ with all fields") + #print "\n~~~~~~~~~ create all princ with all fields ~~~~~" + self._locAddprinc(passw, 'all') #110 =22 + self._locModprinc('-expire "2029-12-30 7pm" all') #112 + self._locModprinc('-pwexpire 12/30/2029 all') #114 + #self._locModprinc('-expire "now+10years" all') #112 + #self._locModprinc('-pwexpire now+10years all') #114 + self._locGetprinc('all') #115 + self._locModprinc('-maxlife 100days all') #116 + self._locGetprinc('all') #117 + self._locModprinc('-maxrenewlife 100days all') #118 + self._locGetprinc('all') #119 + self._locModprinc('+allow_postdated +allow_forwardable all') #120 =30 + self._locModprinc('+allow_proxiable +allow_dup_skey all') #121 + self._locModprinc('+requires_preauth +allow_svr +needchange all') #122 + self._locModprinc('-policy testpolicy4 all') #123 ########### + self._locGetprinc('all') #124 =34 + + # Testing stuff + self._writeHeader("TEST: initial mkey list") #126 + self._writeLine("===== Listing mkeys at start of test") #I add + self._listmkeys() #127 =35 + + self._writeLine( "Testing krb5kdc list_mkeys Done ==============================================") #128 + + self._writeLine("---------------\n xxxxxxxxxx \/\/\/ ERRORS (multiple) EXPECTED below xxxxxxxxxx") + self._writeLine("\nERRORS (multiple) EXPECTED below") + self._writeLine("Testing bogus use_mkey (setting only mkey to future date, using non-existent kvno, so should return error) =======") #129, 130 + self._writeLine( "-> must have a mkey currently active (setting mkey to 2 days from now), should fail and return error") #132 + self._usemkey('1', 'now+2days', False) #133-138 =36 + + self._writeLine("-> must have a mkey currently active (setting mkey to 2019 the future), should fail and return error") #140 + self._usemkey('1', '5/30/2019', False) #141 =37 + self._writeLine("-> bogus kvno and setting mkey to 2 days from now, should fail and return error") #147 + self._usemkey('2', 'now+2days', False) #148 =38 + self._writeLine("-> bogus kvno, should fail and return error") #I add + self._usemkey('2', 'now-2days', False) #I add =39 + self._writeLine( "^^^ABOVE^^ SHOULD HAVE *ALL* FAILED\n-----------------") + + self._writeLine( "Listing mkeys at end of test") #I add + self._listmkeys() #155 =40 + self._writeLine("Testing bogus use_mkey (setting only mkey to future date) Done ===========================") #156 + + + self._writeLine("\nmake sure cpw [change password] works") #158 + # this changes the password of 'test' from 'foobar' in "add, get, and list princs" above + self._locCpw('test1', 'test') #159 =41 + + self._writeHeader("TEST: bogus purge_mkeys (should be no keys purged, no error returned") + #print "\nTesting bogus purge_mkeys (should be no keys purged, no error returned) ===========================" #161 + self._purgemkeys() #162 =42 + self._writeLine("Testing bogus purge_mkeys (no error) Done ===========================") #163 + + self._writeHeader( "add kvno 2") #164 + + self._addmkey('abcde', '-s') #165-167 =43 + self._writeLine(".\nlist mkeys") + self._listmkeys() #169 =44 + + #start daemons + #@@@@@@@@@@@@@@@@@@@@@@@@@@@############@@@@@@@@@@@@@@@@@@############ + self._startkdc() #172 =45 46 + self._writeLine("make sure kdc is up, by kinit test") #176 + self._kinit('test1', 'test') #177 =47 + + self._writeLine("---------------\n\/\/\/ ERROR EXPECTED below. Test passwd policy.:") #180 + self._kinit(passw, 'all', succeed=False) #181 =48 + self._writeLine("^^ABOVE^^ SHOULD HAVE FAILED\n-----------------") + + #change passwd on client's side + + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + self._kpasswd(passw, 'Test123.', 'all')#184-188 =49 !!!!!!!!!!!!!!!! + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + self._kinit('Test123.', 'all') #189 =50 + self._klist() #190 =51 + + self._writeHeader("TEST: password history for principal 'all', new passwords must not be a previous password") #191 + self._kpasswd('Test123.', 'Foobar2!', 'all') #192-195 =52 + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ''' + self._writeLine("--------------\n\/\/\/ ERROR EXPECTED below") #197 + self._kpasswd('Foobar2!', passw, 'all', succeed=False) #199-202 =53 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n----------") + ''' + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + # this shouldn't change the mkvno for any princs (should be 1) #206 + #self._updatePrincEnc() #207 + # princs should still be protected by mkvno 1 #208 + self._writeLine("@@@@@@@@ Wait for other people to fix bug in code 6507 update_princ_encryption to use mkey instead of latest mkey @@@@@@@@@@@@@\n") + self._locGetprincFind('test', 'MKey: vno 1') #209 =54 + + self._purgemkeys() #210 =55 + self._listmkeys() #211 =56 + self._usemkey('2', 'now-1day') #213 =57 + self._listmkeys() #214 =58 + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ''' + self._writeLine("-----------\n\/\/\/ ERROR EXPECTED below") #216 + self._kpasswd('Foobar2!', passw, 'all', succeed=False) #217-221 =59 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n--------") + ''' + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + self._kpasswd('Foobar2!', 'Barfoo3.', 'all') #224-228 =60 + self._kinit('Barfoo3.', 'all') #229 + self._klist() #230 =62 + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ''' + self._writeLine("-------------\n\/\/\/ ERROR EXPECTED below") #231 + self._kpasswd('Barfoo3.', 'Foobar2!', 'all',succeed=False) #233-235 =63 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n---------") + ''' + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + self._writeLine("\nTest's key should be protected by mkvno 2" ) #239 + self._locCpw('foo', 'test') #240 =64 + self._locGetprincFind('test', 'MKey: vno 2') #241 =65 + self._kdestroy() #242 =66 + + self._writeHeader("TEST: krb5kdc refetch of mkey")#243 + self._kinit('foo', 'test') #244 =67 + self._klist() #245 =68 + self._writeLine("END. Testing krb5kdc refetch of mkey list Done ==============================================\n") #246 + + self._updatePrincEnc() #247 =69 + self._get_princ_records() #248 =70 -83 + self._kdestroy() #249 =84 + self._kinit('foo', 'test') #250 =85 + self._purgemkeys() #252 =86 + + #self._stash() #254 =87 #!!! Not necessary in ldap, done by 'create -s' + self._shell(self.clients+'klist/klist' +" -ekt "+self.sandir+"/krb5kdc/.k5.EXAMPLE.ORG") #255=88 + + self._locGetprinc('K/M') #256 =89 + self._purgemkeys() #257 =90 + self._locGetprinc('K/M') #258 + self._listmkeys() #259 =92 + self._kdestroy() #260 + self._kinit('foo', 'test') #261 + self._klist() #262 =95 + + self._writeLine("\n Adding in Master Key Number 3") + self._listmkeys() #265 =96 + self._addmkey('abcde') #266-268 + self._listmkeys() #270 =98 + self._locCpw('foo', 'all') #271 + self._locGetprinc('all') #272 =100 + self._usemkey('3', 'now') #273 + self._listmkeys() #274 =102 + self._locCpw('99acefghI0!', 'all') #275 + self._locGetprinc('all') #276 =104 + self._kdestroy() #277 + self._kinit('foo', 'test') #279 =106 + self._klist() #280 + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'listprincs'") #281 =108 + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'getprinc test'") #282 =109 + + self._writeHeader("TEST: add_mkey with aes128 enctype") #283 + self._addmkey('abcde', '-e aes128-cts-hmac-sha1-96') #284-287 =110 + #!!!!!!!!!!!!!!!!Start to have problems !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + self._listmkeys() #288 =111 + '''$ kdb5_util list_mkeys +kdb5_util: Unable to decrypt latest master key with the provided master key + while getting master key list +kdb5_util: Warning: proceeding without master key list +kdb5_util: master keylist not initialized'''#!!!!!!!!!!!!!!!!!!!!!!! + #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + self._writeLine( "END. Testing add_mkey with aes128 enctype done ==============================================")#289 + self._writeHeader("TEST: krb5kdc refetch of mkey list") + #!!!!!!!!!!!!!!\/ errors \/ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + self._usemkey('4', 'now') #290 =112 + self._listmkeys() #291 =113 + #!!!!!!!!!!!!!!/\ errors /\ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + self._shell(self.kadmin+" -p haoqili/admin -w "+passw+" -q 'cpw -pw abcde test'") #292 =114 + self._shell(self.kadmin +" -p haoqili/admin -w "+passw+" -q 'getprinc test'") #293 + + self._kdestroy() #294 =116 + + self._writeLine("\nTesting krb5kdc refetch of mkey list =================================================") #295 + self._kinit('abcde', 'test') #296 =117 + self._klist() #297 =118 + self._writeLine("Testing krb5kdc refetch of mkey list Done :) =================================================\n") #298 + + self._killkdc() #300 =119, 120 + self._startkdc() #301 =121 122 + + # The lines below are commented out because krb5kdc could not be restarted. For their error messages, see the outfile + ''' + kdc.log: + Aug 31 12:21:23 reach-my-dream krb5kdc[24273](info): AS_REQ (2 etypes {16 17}) 127.0.1.1: ISSUE: authtime 1251746483, etypes {rep=16 tkt=18 ses=16}, test@EXAMPLE.ORG for krbtgt/EXAMPLE.ORG@EXAMPLE.ORG +krb5kdc: Unable to decrypt latest master key with the provided master key + - while fetching master keys list for realm EXAMPLE.ORG + ''' + ''' + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'cpw -pw foo test'") #304 =123 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc test'") #305 =124 + self._kdestroy() #307 =125 + + self._writeLine("\nTesting krb5kdc refetch of mkey list =================================================") #308 + self._kinit('foo', 'test') #309 =126 + self._klist() #310 =127 + self._writeLine("Testing krb5kdc refetch of mkey list Done =================================================\n") #311 + + self._updatePrincEnc() #313 =128 + self._locGetprinc('K/M') #314 + self._locGetprinc('all') #315 =130 + self._locGetprinc('haoqili') #316 + self._kdestroy() #317 =132 + self._kinit('foo', 'test') #318 + self._stash() #319 =134 + self._shell(self.clients+'klist/klist' +" -ekt "+self.sandir+"/krb5kdc/.k5.EXAMPLE.ORG") #320 + self._locGetprinc('K/M') #321 =136 + self._purgemkeys() #322 + self._locGetprinc('K/M') #323 =138 + self._locGetprinc('all') #324 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc test'") #325 =140 + self._listmkeys() #326 + self._kdestroy() #327 =142 + self._kinit('foo', 'test') #328 + self._klist() #329 =144 + + self._get_princ_records() #330 =145-158 + + self._writeHeader("TEST: add_meky with DES-crc enctype") + #print "\nTesting add_mkey with DES-crc enctype ==============================================" #331 + self._addmkey('abcde', '-e des-cbc-crc') #332-335 =159 + self._listmkeys() #336 =160 + self._writeLine( "END. Testing add_mkey with DES-crc enctype Done ==============================================") #337 + self._addmkey('abcde') #338-341 =161 + self._listmkeys() #342 =162 + self._writeLine( "current time: "+strftime("%Y-%m-%d %H:%M:%S") ) #343 + + self._usemkey('5', 'now-1day') #344 =163 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#345 + self._listmkeys() #346 =164 + self._usemkey('5', 'now') #347 =165 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#348 + self._listmkeys() #349 =166 + self._usemkey('5', 'now+3days') #350 =167 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#351 + self._listmkeys() #352 =168 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#353 + self._usemkey('5', 'now+5sec') #354 =169 + self._listmkeys() #355 =170 + time.sleep(5) #356 + self._listmkeys() #357 =171 + self._writeLine("current time: "+strftime("%Y-%m-%d %H:%M:%S") )#358 + self._usemkey('4', 'now+5sec') #359 =172 + self._listmkeys() #360 =173 + time.sleep(5) #361 + self._listmkeys() #362 =174 + self._usemkey('5', 'now+3days') #363 =175 + + self._writeLine("------------\n\/\/\/ ERROR EXPECTED below" )#364 + self._writeLine("should fail, because there must be one mkey currently active") #365 + self._usemkey('4', 'now+2days', False) #366 =176 + self._writeLine("^^^ABOVE^^ SHOULD HAVE FAILED\n---------------") + + self._listmkeys() #373 =177 + self._usemkey('4', '1/30/2009') #375 =178 + + self._writeHeader("TEST: purge_mkeys (removing mkey 5)") + #print "\nTesting purge_mkeys (removing mkey 5) ==============================================" #378 + self._purgemkeys() #379 =179 + #self._stash() #380 =180 + self._shell(self.clients+'klist/klist' +" -ekt "+self.sandir+"/krb5kdc/.k5.EXAMPLE.ORG") #381=181 + self._listmkeys() #382 =182 + self._shell("kadmin -p haoqili/admin -w "+passw+" -q 'getprinc K/M'") #383 =183 + self._writeLine("Testing purge_mkeys Done ==============================================") #384 + self._writeHeader("MASTER KEY MIGRATION TEST DONE. please consult 'outfile' in your sandbox for more info. The sandbox is at: %s" % self.sandir) + # I added + self._sysexit(finished=True) + ''' +#################################################### +#################################################### + +class Launcher: + #def __init__(self, path, sandP): + #def __init__(self): + def __init__(self, sandP): + self._buildDir = os.environ["PWD"] + self._confDir = '%s/tests/mk_migr/ldap_backend/input_conf' % self._buildDir + + #setting up sandbox + if sandP != "": + self._sandP = sandP + else: #default + self._sandP = '%s/tests/mk_migr/ldap_backend/sandbox' %self._buildDir + + print self._sandP + print "sandP" + self._vars = {'sandir': self._sandP, + 'localFQDN': socket.getfqdn()} + + def _prepSandbox(self, sandir): + if os.path.exists(sandir): + shutil.rmtree(sandir) + print "------about to make sandbox, with the path of:" + print sandir + os.makedirs(sandir, 0777) + os.mkdir(sandir+'/krb5kdc', 0777) + print "------sandbox made" + + def _createFileFromTemplate(self, outpath, template, vars): + fin = open(template, 'r') + result = fin.read() % vars + fin.close() + fout = open(outpath, 'w') + fout.write(result) + fout.close() + + ####### Launcher RUN ################ + def runLauncher(self): + # create sandbox file directory (and sandbox/krb5kdc) if it does not exit + self._prepSandbox(self._sandP) + + # Export the 3 env lines + src_path=os.environ["PWD"] + os.environ["LD_LIBRARY_PATH"] = '%s/lib' % src_path + + str1 = '%s/krb5.conf' % self._sandP + os.environ["KRB5_CONFIG"] = str1 + + str2 = '%s/kdc.conf' % self._sandP + os.environ["KRB5_KDC_PROFILE"] = str2 + + str3 = '%s/kadm5.acl' % self._sandP + + # Create adequate to the environment config files + self._createFileFromTemplate(str1, '%s/%s' % (self._confDir, 'krb5_template_ldap.conf'), self._vars) + self._createFileFromTemplate(str2, '%s/%s' % (self._confDir, 'kdc_template_ldap.conf'), self._vars) + self._createFileFromTemplate(str3, '%s/%s' % (self._confDir, 'kadm5_template_ldap.acl'), self._vars) + + return (self._confDir, self._sandP) + +#################################################### +#################################################### + +def makeBool(aStr): + if aStr == "True" or aStr == "T": + return True + if aStr == "False" or aStr == "F": + return False + else: + print "did NOT execute due to invalid True False argument. Please enter either 'True', 'T', 'False', or 'F'" + sys.exit() + +# # # # # # # # # # # # # # # # # # # # # # # # # + +def processInputs(parser): +#def processInputs(): + + # get inputs + (options, args) = parser.parse_args() + + verbose = makeBool(options.opVerbose) + pw = options.opPassword + + kdcPath = options.opKdcPath #1 + kdmdPath = options.opKdmdPath #2 + kdbPath = options.opKdbPath #3a + ldapPath = options.opLdapPath #3b + kdmlPath = options.opKdmlPath #4 + kdmPath = options.opKdmPath #5 + cltPath = options.opCltPath #6 + + sandPath = options.opSandbox + + ########### Launch ############### + + print "\n############ Start Launcher #############" + myLaunch = Launcher(sandPath) + (confDir, sandPath) = myLaunch.runLauncher() + + print ":D" + print sandPath + + test = LDAPbackendSetup(verbose, pw, kdcPath, kdmdPath, kdbPath, ldapPath, kdmlPath, kdmPath, cltPath, sandPath, confDir) + print "########## Finished Launcher ############\n" + + return test +# # # # # # # # # # # # # # # # # # # # # # # # # + +def makeParser(): + usage = "\n\t%prog [-v][-p][-c][-d][-b][-l][-t][-s]" + description = "Description:\n\tTests for the master key migration commands." + parser = OptionParser(usage=usage, description=description) + + parser.add_option("-v", "--verbose", type="string", dest="opVerbose", +default="True", help="'True' or 'False'. Switch on for details of command lines and outputs. Default is 'True'") + + parser.add_option("-p", "--password", type="string", dest="opPassword", default="test123", help="master password for many of the passwords in the test. Default is 'test123'") + + ## Default Paths + dSrcPath = src_path=os.environ["PWD"] + dKdcPath = '%s/kdc/krb5kdc' % dSrcPath #1 + dKdmdPath = '%s/kadmin/server/kadmind' % dSrcPath #2 + dKdbPath = '%s/kadmin/dbutil/kdb5_util' % dSrcPath #3a + dLdapPath = '%s/plugins/kdb/ldap/ldap_util/kdb5_ldap_util' % dSrcPath #3b + dKdmlPath = '%s/kadmin/cli/kadmin.local' % dSrcPath #4 + dKdmPath = '%s/kadmin/cli/kadmin' % dSrcPath #5 + dCltPath = '%s/clients' % dSrcPath #6 + + parser.add_option("-c", "--krb5kdcpath", +type="string", dest="opKdcPath", +default=dKdcPath, help="set krb5kdc path, default="+dKdcPath) #1 + + parser.add_option("-d", "--kadmindpath", +type="string", dest="opKdmdPath", +default=dKdmdPath, help="set kadmind path, default="+dKdmdPath) #2 + + parser.add_option("-b", "--kdb5_utilpath", +type="string", dest="opKdbPath", +default=dKdbPath, help="set kdb5_util path, default="+dKdbPath) #3a + + parser.add_option("-a", "--kdb5_ldap_utilpath", +type="string", dest="opLdapPath", +default=dKdbPath, help="set kdb5_ldap_util path, default="+dLdapPath) #3b + + parser.add_option("-l", "--kadminlocalpath", +type="string", dest="opKdmlPath", +default=dKdmlPath, help="set kadmin.local path, default="+dKdmlPath) #4 + + parser.add_option("-n", "--kadminpath", +type="string", dest="opKdmPath", +default=dKdmPath, help="set kadmin path, default="+dKdmPath) #5 + + parser.add_option("-t", "--clientspath", +type="string", dest="opCltPath", +default=dCltPath, help="set clients path, default="+dCltPath) #6 + + # set up / initializing stuff for the sandbox + parser.add_option("-s", "--sandbox", +type="string", dest="opSandbox", +default="", +help="path for the sandbox. Default is 'src/tests/mk_migr/ldap_backend/sandbox'") + + return parser + +#################################################### +if __name__ == '__main__': + #processInputs() + + parser = makeParser() + test = processInputs(parser) + result = test.run() diff --git a/src/util/collected-client-lib/Makefile.in b/src/util/collected-client-lib/Makefile.in index bd8b5d3aba..68ede98494 100644 --- a/src/util/collected-client-lib/Makefile.in +++ b/src/util/collected-client-lib/Makefile.in @@ -43,7 +43,7 @@ STOBJLISTS= \ ../../lib/crypto/krb/crc32/OBJS.ST \ ../../lib/crypto/builtin/des/OBJS.ST \ ../../lib/crypto/krb/dk/OBJS.ST \ - ../../lib/crypto/krb/enc_provider/OBJS.ST \ + ../../lib/crypto/builtin/enc_provider/OBJS.ST \ ../../lib/crypto/krb/hash_provider/OBJS.ST \ ../../lib/crypto/krb/keyhash_provider/OBJS.ST \ ../../lib/crypto/builtin/md4/OBJS.ST \ diff --git a/src/util/support/fake-addrinfo.c b/src/util/support/fake-addrinfo.c index 4b628bb67b..34ce7701bd 100644 --- a/src/util/support/fake-addrinfo.c +++ b/src/util/support/fake-addrinfo.c @@ -140,7 +140,15 @@ extern /*@dependent@*/ char *gai_strerror (int code) /*@*/; #endif #if defined (__linux__) && defined(HAVE_GETADDRINFO) -# define COPY_FIRST_CANONNAME +/* Define COPY_FIRST_CANONNAME for glibc 2.3 and prior. */ +#include +# ifdef __GLIBC_PREREQ +# if ! __GLIBC_PREREQ(2, 4) +# define COPY_FIRST_CANONNAME +# endif +# else +# define COPY_FIRST_CANONNAME +# endif #endif #ifdef _AIX @@ -1157,7 +1165,7 @@ getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint, return aierr; } - /* Linux libc version 6 (libc-2.2.4.so on Debian) is broken. + /* Linux libc version 6 prior to 2.3.4 is broken. RFC 2553 says that when AI_CANONNAME is set, the ai_canonname flag of the first returned structure has the canonical name of @@ -1188,9 +1196,12 @@ getaddrinfo (const char *name, const char *serv, const struct addrinfo *hint, Ref: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=133668 . Since it's dependent on the target hostname, it's hard to check - for at configure time. Always do it on Linux for now. When - they get around to fixing it, add a compile-time or run-time - check for the glibc version in use. + for at configure time. The bug was fixed in glibc 2.3.4. + After the fix, the ai_canonname field is allocated, so our + workaround leaks memory. We disable the workaround for glibc + >= 2.4, but there is no easy way to test for glibc patch + versions, so we still leak memory under glibc 2.3.4 through + 2.3.6. Some Windows documentation says that even when AI_CANONNAME is set, the returned ai_canonname field can be null. The NetBSD