summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2009-09-13 02:52:23 +0000
committerGreg Hudson <ghudson@mit.edu>2009-09-13 02:52:23 +0000
commit0e39f8a3ad915eeb0131fb4a87b0fef304101cfd (patch)
tree6c6d7fd4b23f4724156300b5505433b13cfe9fb6 /src/lib
parentf89b62fe9fd7b0cb10d7e2ff542fb18c1b56d35d (diff)
downloadkrb5-0e39f8a3ad915eeb0131fb4a87b0fef304101cfd.tar.gz
krb5-0e39f8a3ad915eeb0131fb4a87b0fef304101cfd.tar.xz
krb5-0e39f8a3ad915eeb0131fb4a87b0fef304101cfd.zip
Implement s4u extensions
Merge Luke's users/lhoward/s4u branch to trunk. Implements S4U2Self and S4U2Proxy extensions. ticket: 6563 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22736 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/crypto/krb/enc_provider/Makefile.in41
-rw-r--r--src/lib/crypto/krb/enc_provider/aes.c415
-rw-r--r--src/lib/crypto/krb/enc_provider/deps49
-rw-r--r--src/lib/crypto/krb/enc_provider/des.c181
-rw-r--r--src/lib/crypto/krb/enc_provider/des3.c221
-rw-r--r--src/lib/crypto/krb/enc_provider/enc_provider.h36
-rw-r--r--src/lib/crypto/krb/enc_provider/rc4.c271
-rw-r--r--src/lib/gssapi/generic/gssapi_ext.h31
-rw-r--r--src/lib/gssapi/krb5/Makefile.in3
-rw-r--r--src/lib/gssapi/krb5/accept_sec_context.c68
-rw-r--r--src/lib/gssapi/krb5/acquire_cred.c5
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h52
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.c14
-rw-r--r--src/lib/gssapi/krb5/import_name.c22
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c82
-rw-r--r--src/lib/gssapi/krb5/krb5_gss_glue.c1
-rw-r--r--src/lib/gssapi/krb5/s4u_gss_glue.c346
-rw-r--r--src/lib/gssapi/krb5/val_cred.c3
-rw-r--r--src/lib/gssapi/libgssapi_krb5.exports2
-rw-r--r--src/lib/gssapi/mechglue/Makefile.in3
-rw-r--r--src/lib/gssapi/mechglue/g_accept_sec_context.c185
-rw-r--r--src/lib/gssapi/mechglue/g_acquire_cred.c70
-rw-r--r--src/lib/gssapi/mechglue/g_acquire_cred_imp_name.c549
-rw-r--r--src/lib/gssapi/mechglue/g_glue.c16
-rw-r--r--src/lib/gssapi/mechglue/g_initialize.c3
-rw-r--r--src/lib/gssapi/mechglue/g_set_context_option.c4
-rw-r--r--src/lib/gssapi/mechglue/mglueP.h31
-rw-r--r--src/lib/gssapi/spnego/gssapiP_spnego.h31
-rw-r--r--src/lib/gssapi/spnego/spnego_mech.c143
-rw-r--r--src/lib/kadm5/str_conv.c12
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c41
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h4
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c34
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c12
-rw-r--r--src/lib/krb5/krb/Makefile.in3
-rw-r--r--src/lib/krb5/krb/gc_frm_kdc.c21
-rw-r--r--src/lib/krb5/krb/gc_via_tkt.c70
-rw-r--r--src/lib/krb5/krb/get_creds.c69
-rw-r--r--src/lib/krb5/krb/get_in_tkt.c168
-rw-r--r--src/lib/krb5/krb/int-proto.h20
-rw-r--r--src/lib/krb5/krb/kfree.c27
-rw-r--r--src/lib/krb5/krb/preauth2.c58
-rw-r--r--src/lib/krb5/krb/s4u_creds.c829
-rw-r--r--src/lib/krb5/krb/send_tgs.c115
-rw-r--r--src/lib/krb5/krb/srv_dec_tkt.c71
-rw-r--r--src/lib/krb5/libkrb5.exports16
-rw-r--r--src/lib/krb5/os/sendto_kdc.c2
47 files changed, 4099 insertions, 351 deletions
diff --git a/src/lib/crypto/krb/enc_provider/Makefile.in b/src/lib/crypto/krb/enc_provider/Makefile.in
new file mode 100644
index 000000000..2eedf1d9d
--- /dev/null
+++ b/src/lib/crypto/krb/enc_provider/Makefile.in
@@ -0,0 +1,41 @@
+thisconfigdir=../../../..
+myfulldir=lib/crypto/krb/enc_provider
+mydir=lib/crypto/krb/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)/.. -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= des.o des3.o rc4.o aes.o
+
+OBJS= \
+ $(OUTPRE)des.$(OBJEXT) \
+ $(OUTPRE)des3.$(OBJEXT) \
+ $(OUTPRE)aes.$(OBJEXT) \
+ $(OUTPRE)rc4.$(OBJEXT)
+
+SRCS= \
+ $(srcdir)/des.c \
+ $(srcdir)/des3.c \
+ $(srcdir)/aes.c \
+ $(srcdir)/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/krb/enc_provider/aes.c b/src/lib/crypto/krb/enc_provider/aes.c
new file mode 100644
index 000000000..060d119c4
--- /dev/null
+++ b/src/lib/crypto/krb/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 "../aead.h"
+
+#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/krb/enc_provider/deps b/src/lib/crypto/krb/enc_provider/deps
new file mode 100644
index 000000000..064976279
--- /dev/null
+++ b/src/lib/crypto/krb/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)/../../builtin/des/des_int.h $(srcdir)/../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/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)/../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/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)/../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/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)/../aead.h $(srcdir)/../cksumtypes.h enc_provider.h \
+ rc4.c
diff --git a/src/lib/crypto/krb/enc_provider/des.c b/src/lib/crypto/krb/enc_provider/des.c
new file mode 100644
index 000000000..547f6b976
--- /dev/null
+++ b/src/lib/crypto/krb/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/krb/enc_provider/des3.c b/src/lib/crypto/krb/enc_provider/des3.c
new file mode 100644
index 000000000..412c994a7
--- /dev/null
+++ b/src/lib/crypto/krb/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 "../aead.h"
+
+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/krb/enc_provider/enc_provider.h b/src/lib/crypto/krb/enc_provider/enc_provider.h
new file mode 100644
index 000000000..92022b3c8
--- /dev/null
+++ b/src/lib/crypto/krb/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/krb/enc_provider/rc4.c b/src/lib/crypto/krb/enc_provider/rc4.c
new file mode 100644
index 000000000..b950a605b
--- /dev/null
+++ b/src/lib/crypto/krb/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 "../aead.h"
+/* 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/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
index 40f5ab809..ce115639b 100644
--- a/src/lib/gssapi/generic/gssapi_ext.h
+++ b/src/lib/gssapi/generic/gssapi_ext.h
@@ -254,6 +254,37 @@ 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 */
+
#ifdef __cplusplus
}
#endif
diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in
index 2ee9e1d9c..645b91b11 100644
--- a/src/lib/gssapi/krb5/Makefile.in
+++ b/src/lib/gssapi/krb5/Makefile.in
@@ -73,6 +73,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 \
@@ -123,6 +124,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) \
@@ -176,6 +178,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 dd17c044b..d340db7e7 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)
@@ -866,6 +913,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 */
{
@@ -1055,8 +1119,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 48471b4f4..4427ed763 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -532,8 +532,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
cred->usage = cred_usage;
cred->princ = 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;
@@ -759,3 +759,4 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
*minor_status = 0;
return GSS_S_COMPLETE;
}
+
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index a1073f344..48da87807 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -162,8 +162,9 @@ typedef struct _krb5_gss_cred_id_rec {
/* name/type of credential */
gss_cred_usage_t usage;
krb5_principal princ; /* this is not interned as a gss_name_t */
- 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;
@@ -466,6 +467,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
@@ -766,6 +790,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 */,
@@ -790,6 +825,19 @@ OM_uint32 gss_krb5int_unseal_token_v3(krb5_context *contextptr,
int gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc);
+/* 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 a20e59dfb..519abb860 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 */
};
diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c
index 440d36222..6879c766f 100644
--- a/src/lib/gssapi/krb5/import_name.c
+++ b/src/lib/gssapi/krb5/import_name.c
@@ -56,8 +56,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
krb5_context context;
krb5_principal princ;
krb5_error_code code;
- unsigned char *cp, *end;
- char *stringrep, *tmp, *tmp2;
+ char *stringrep, *tmp, *tmp2, *cp;
OM_uint32 length;
#ifndef NO_PASSWORD
struct passwd *pw;
@@ -156,12 +155,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
goto do_getpwuid;
#endif
} else if (g_OID_equal(input_name_type, gss_nt_exported_name)) {
-#define BOUNDS_CHECK(cp, end, n) do { if ((end) - (cp) < (n)) \
- goto fail_name; } while (0)
- cp = (unsigned char *)tmp;
- end = cp + input_name_buffer->length;
-
- BOUNDS_CHECK(cp, end, 4);
+ cp = tmp;
if (*cp++ != 0x04)
goto fail_name;
if (*cp++ != 0x01)
@@ -169,28 +163,20 @@ krb5_gss_import_name(minor_status, input_name_buffer,
if (*cp++ != 0x00)
goto fail_name;
length = *cp++;
- if (length != (ssize_t)gss_mech_krb5->length+2)
+ if (length != gss_mech_krb5->length+2)
goto fail_name;
-
- BOUNDS_CHECK(cp, end, 2);
if (*cp++ != 0x06)
goto fail_name;
length = *cp++;
if (length != gss_mech_krb5->length)
goto fail_name;
-
- BOUNDS_CHECK(cp, end, length);
if (memcmp(cp, gss_mech_krb5->elements, length) != 0)
goto fail_name;
cp += length;
-
- BOUNDS_CHECK(cp, end, 4);
length = *cp++;
length = (length << 8) | *cp++;
length = (length << 8) | *cp++;
length = (length << 8) | *cp++;
-
- BOUNDS_CHECK(cp, end, length);
tmp2 = malloc(length+1);
if (tmp2 == NULL) {
xfree(tmp);
@@ -198,7 +184,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
krb5_free_context(context);
return GSS_S_FAILURE;
}
- strncpy(tmp2, (char *)cp, length);
+ strncpy(tmp2, cp, length);
tmp2[length] = 0;
stringrep = tmp2;
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 5559fadbc..0bb4fde02 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -128,25 +128,69 @@ 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;
- if ((code = krb5_copy_principal(context, cred->princ, &in_creds.client)))
+ if ((code = krb5_cc_get_principal(context, cred->ccache, &cc_princ)))
goto cleanup;
- if ((code = krb5_copy_principal(context, server, &in_creds.server)))
- goto cleanup;
- in_creds.times.endtime = endtime;
- in_creds.keyblock.enctype = 0;
+ /*
+ * 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)) {
+ krb5_creds mcreds;
+
+ flags |= KRB5_GC_CANONICALIZE |
+ KRB5_GC_NO_STORE |
+ KRB5_GC_CONSTRAINED_DELEGATION;
+
+ memset(&mcreds, 0, sizeof(mcreds));
- code = krb5_get_credentials(context, 0, cred->ccache,
+ mcreds.magic = KV5M_CREDS;
+ mcreds.times.endtime = cred->tgt_expire;
+ mcreds.server = cc_princ;
+ mcreds.client = cred->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->princ;
+ }
+
+ in_creds.server = server;
+ in_creds.times.endtime = endtime;
+
+ 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->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
@@ -159,10 +203,10 @@ static krb5_error_code get_credentials(context, cred, server, now,
}
cleanup:
- if (in_creds.client)
- krb5_free_principal(context, in_creds.client);
- if (in_creds.server)
- krb5_free_principal(context, in_creds.server);
+ if (cc_princ)
+ krb5_free_principal(context, cc_princ);
+ krb5_free_cred_contents(context, &evidence_creds);
+
return code;
}
struct gss_checksum_data {
@@ -390,8 +434,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,
@@ -931,12 +975,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 f9bf03016..034550122 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 000000000..8e2d690b1
--- /dev/null
+++ b/src/lib/gssapi/krb5/s4u_gss_glue.c
@@ -0,0 +1,346 @@
+/* -*- 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 <memory.h>
+#endif
+#include <assert.h>
+
+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_principal 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;
+ in_creds.server = impersonator_cred->princ;
+
+ if (impersonator_cred->req_enctypes != NULL)
+ in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0];
+
+ 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) {
+ *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_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_principal)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->princ == NULL ||
+ impersonator_cred->proxy_cred) {
+ code = G_BAD_USAGE;
+ goto cleanup;
+ }
+
+ 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 = krb5_copy_principal(context, subject_creds->client, &cred->princ);
+ 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->princ :
+ (krb5_principal)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);
+ if (cred->ccache != NULL)
+ krb5_cc_destroy(context, cred->ccache);
+ if (cred->princ != NULL)
+ krb5_free_principal(context, cred->princ);
+ xfree(cred);
+ }
+
+ return major_status;
+}
+
diff --git a/src/lib/gssapi/krb5/val_cred.c b/src/lib/gssapi/krb5/val_cred.c
index dd82d5341..43b1f695d 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->princ)) {
+ if (!cred->proxy_cred &&
+ !krb5_principal_compare(context, princ, cred->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 69f390e45..d641fc65b 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 927b3755c..18e89f19d 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 \
@@ -58,6 +59,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) \
@@ -102,6 +104,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 fa703d34d..dc4391593 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 fada9e887..6dfc65f7b 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 <errno.h>
#include <time.h>
-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 000000000..9ba6a1faa
--- /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 <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+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 5a8ea54b1..4d35819c5 100644
--- a/src/lib/gssapi/mechglue/g_glue.c
+++ b/src/lib/gssapi/mechglue/g_glue.c
@@ -611,25 +611,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 85fbe6321..e34b7bf0a 100644
--- a/src/lib/gssapi/mechglue/g_initialize.c
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -761,6 +761,9 @@ 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);
+ /* New for 1.8 */
+ GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_acquire_cred_impersonate_name);
+ GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_add_cred_impersonate_name);
assert(mech_type != GSS_C_NO_OID);
diff --git a/src/lib/gssapi/mechglue/g_set_context_option.c b/src/lib/gssapi/mechglue/g_set_context_option.c
index 17d9e3bac..b35b36ad5 100644
--- a/src/lib/gssapi/mechglue/g_set_context_option.c
+++ b/src/lib/gssapi/mechglue/g_set_context_option.c
@@ -70,8 +70,8 @@ gss_set_sec_context_option (OM_uint32 *minor_status,
}
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 001636146..46bfb9463 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 */
+ /* */);
+
} *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 e1f3987cd..5e6cd5a0c 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 */
@@ -333,6 +343,15 @@ spnego_gss_inquire_sec_context_by_oid
);
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
(
OM_uint32 *minor_status,
@@ -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 */
+
#ifdef __cplusplus
}
#endif
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index a2b926dc3..14b65f751 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 */
@@ -257,7 +257,9 @@ static struct gss_config spnego_mechanism =
spnego_gss_wrap_iov,
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 */
};
#ifdef _GSS_STATIC_LINK
@@ -1787,6 +1789,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(
@@ -1942,6 +2014,9 @@ spnego_gss_delete_sec_context(
*/
if (*ctx != NULL &&
(*ctx)->magic_num == SPNEGO_MAGIC_ID) {
+ (void) gss_delete_sec_context(minor_status,
+ &(*ctx)->ctx_handle,
+ output_token);
(void) release_spnego_ctx(ctx);
} else {
ret = gss_delete_sec_context(minor_status,
@@ -2088,6 +2163,21 @@ spnego_gss_inquire_sec_context_by_oid(
}
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,
gss_ctx_id_t *context_handle,
@@ -2217,6 +2307,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);
+}
+
/*
* We will release everything but the ctx_handle so that it
* can be passed back to init/accept context. This routine should
diff --git a/src/lib/kadm5/str_conv.c b/src/lib/kadm5/str_conv.c
index 2bd99adbc..51637f7de 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/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 1917d8974..b1b09371b 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 7444443ba..fc62c8f4e 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 ed01b7560..cd63ffbb9 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),
@@ -1323,6 +1355,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 7a08ec888..a2e9c0a4d 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -1061,6 +1061,18 @@ decode_krb5_pa_for_user(const krb5_data *code, krb5_pa_for_user **repptr)
}
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)
{
setup_buf_only(krb5_pa_pac_req *);
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index 9715e6dfc..8b8f6d2db 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -79,6 +79,7 @@ STLIBOBJS= \
rd_req_dec.o \
rd_safe.o \
recvauth.o \
+ s4u_creds.o \
sendauth.o \
send_tgs.o \
ser_actx.o \
@@ -167,6 +168,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) \
@@ -256,6 +258,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 3098e8e13..b3144c84e 100644
--- a/src/lib/krb5/krb/gc_frm_kdc.c
+++ b/src/lib/krb5/krb/gc_frm_kdc.c
@@ -1007,6 +1007,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
@@ -1028,9 +1033,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",
@@ -1048,9 +1051,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. */
@@ -1090,9 +1091,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;
@@ -1257,9 +1256,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 83c8026fc..273655ab5 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
@@ -159,12 +159,34 @@ 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;
krb5_error *err_reply;
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 c02ddedc6..dad3e1a91 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 018676dbe..63594ddfd 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 b81fe2566..cc0c9f2de 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 bec9a61bf..8cef95431 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)
}
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 e6f4215d5..996cbfd36 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[] = {
{
@@ -1751,6 +1804,11 @@ static const pa_types_t pa_types[] = {
PA_INFO,
},
{
+ KRB5_PADATA_S4U_X509_USER,
+ pa_s4u_x509_user,
+ PA_INFO,
+ },
+ {
-1,
NULL,
0,
diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
new file mode 100644
index 000000000..613bbef1f
--- /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 97cd02bf7..eee47ed57 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 b5cf260f2..0934e27e1 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 bd50fddb5..b809e83cf 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -20,11 +20,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
@@ -60,10 +61,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
@@ -71,6 +73,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
@@ -134,9 +137,9 @@ krb5_auth_con_setsendsubkey
krb5_auth_con_setuseruserkey
krb5_auth_to_rep
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
@@ -243,8 +246,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
@@ -284,6 +288,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
@@ -380,7 +386,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
@@ -529,6 +534,7 @@ krb5int_find_authdata
krb5int_find_pa_data
krb5int_foreach_localaddr
krb5int_free_addrlist
+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 dcf08d996..3e5f9e234 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;