diff options
| author | Greg Hudson <ghudson@mit.edu> | 2009-09-13 02:52:23 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2009-09-13 02:52:23 +0000 |
| commit | 0e39f8a3ad915eeb0131fb4a87b0fef304101cfd (patch) | |
| tree | 6c6d7fd4b23f4724156300b5505433b13cfe9fb6 /src/lib | |
| parent | f89b62fe9fd7b0cb10d7e2ff542fb18c1b56d35d (diff) | |
| download | krb5-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')
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; |
