CPPFLAGS="$CPPFLAGS $KRB5CFLAGS"
save_LIBS="$LIBS"
LIBS="$LIBS $KRB5LIBS"
- AC_CHECK_FUNCS([add_to_error_table add_error_table krb5_princ_size krb5_principal_get_comp_string encode_krb5_enc_tkt_part encode_krb5_ticket krb5_c_encrypt krb5_c_encrypt_length krb5_cc_register krb5_decode_ticket krb5_get_prompt_types krb5_allow_weak_crypto krb5_enctype_enable])
+ AC_CHECK_FUNCS([add_to_error_table add_error_table krb5_princ_size krb5_principal_get_comp_string encode_krb5_enc_tkt_part encode_krb5_ticket krb5_c_encrypt krb5_c_encrypt_length krb5_c_decrypt krb5_cc_register krb5_decode_ticket krb5_get_prompt_types krb5_allow_weak_crypto krb5_enctype_enable krb5_crypto_init krb5_encrypt_tkt_part krb5_decrypt_tkt_part])
AC_CHECK_FUNCS([krb5_524_convert_creds], ,
[AC_CHECK_FUNCS([krb524_convert_creds_kdc], ,
[AC_CHECK_LIB([krb524], [krb524_convert_creds_kdc],
AC_CHECK_HEADERS([kerberosIV/krb.h])
AC_CHECK_HEADERS([kerberosV/heim_err.h])
-AC_MSG_CHECKING(for krb5_creds.keyblock existence)
-AC_CACHE_VAL(ac_cv_krb5_creds_keyblock_exists,
-[
-AC_TRY_COMPILE(
-[#include <krb5.h>],
-[krb5_creds _c;
-printf("%x\n", _c.keyblock);],
-ac_cv_krb5_creds_keyblock_exists=yes,
-ac_cv_krb5_creds_keyblock_exists=no)])
-AC_MSG_RESULT($ac_cv_krb5_creds_keyblock_exists)
-
-AC_MSG_CHECKING(for krb5_creds.session existence)
-AC_CACHE_VAL(ac_cv_krb5_creds_session_exists,
-[
-AC_TRY_COMPILE(
-[#include <krb5.h>],
-[krb5_creds _c;
-printf("%x\n", _c.session);],
-ac_cv_krb5_creds_session_exists=yes,
-ac_cv_krb5_creds_session_exists=no)])
-AC_MSG_RESULT($ac_cv_krb5_creds_session_exists)
-AC_MSG_CHECKING(for krb5_prompt.type existence)
-AC_CACHE_VAL(ac_cv_krb5_prompt_type_exists,
-[
-AC_TRY_COMPILE(
-[#include <krb5.h>],
-[krb5_prompt _p;
-printf("%x\n", _p.type);],
-ac_cv_krb5_prompt_type_exists=yes,
-ac_cv_krb5_prompt_type_exists=no)])
-AC_MSG_RESULT($ac_cv_krb5_prompt_type_exists)
-
-if test "x$ac_cv_krb5_creds_keyblock_exists" = "xyes"; then
- AC_DEFINE(HAVE_KRB5_CREDS_KEYBLOCK, 1, [define if krb5_creds has keyblock])
-fi
-if test "x$ac_cv_krb5_creds_session_exists" = "xyes"; then
- AC_DEFINE(HAVE_KRB5_CREDS_SESSION, 1, [define if krb5_creds has session])
-fi
-if test "x$ac_cv_krb5_prompt_type_exists" = "xyes"; then
- AC_DEFINE(HAVE_KRB5_PROMPT_TYPE, 1, [define if krb5_prompt has type])
-fi
-
-dnl AC_CHECK_MEMBERS([krb5_creds.keyblock, krb5_creds.session],,, [#include <krb5.h>])
+ AC_CHECK_MEMBERS([krb5_creds.keyblock, krb5_creds.keyblock.enctype,
+ krb5_creds.session, krb5_keytab_entry.key,
+ krb5_keytab_entry.keyblock, krb5_keyblock.enctype,
+ krb5_keyblock.keytype, krb5_prompt.type], , ,
+ [#include <krb5.h>])
+ AC_CHECK_DECLS([krb5_free_keytab_entry_contents, krb5_kt_free_entry,
+ KRB5_KU_TICKET], [], [], [#include <krb5.h>])
CPPFLAGS="$save_CPPFLAGS"
LIBS="$save_LIBS"
fi
-
if test "x$ac_cv_krb5_cc_register_exists" = "xyes"; then
AC_DEFINE(HAVE_KRB5_CC_REGISTER, 1, [define if krb5_cc_register exists])
fi
--- /dev/null
+/*
+ * Copyright (c) 2013 Chaskiel Grundman <cg2v@andrew.cmu.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+#include <afs/stds.h>
+#include <rx/rx.h>
+#include <rx/rxkad.h>
+
+#include <string.h>
+
+#include <krb5.h>
+
+#ifdef RX_ENABLE_LOCKS
+static afs_kmutex_t krb5_lock;
+#endif
+
+/* these globals are expected to be set only once, so locking is not needed */
+static char *keytab_name;
+static int have_keytab_keys;
+
+/*
+ * krb5_lock must be held to use/set these globals, including any
+ * krb5 api use with k5ctx
+ */
+static krb5_context k5ctx;
+static int nkeys;
+static krb5_keytab_entry *ktent;
+static time_t last_reload;
+
+#ifdef HAVE_KRB5_KEYBLOCK_ENCTYPE
+# define kb_enctype(keyblock) ((keyblock)->enctype)
+#elif defined(HAVE_KRB5_KEYBLOCK_KEYTYPE)
+# define kb_enctype(keyblock) ((keyblock)->keytype)
+#else
+# error Cannot figure out how keyblocks work
+#endif
+#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
+# define kte_keyblock(kte) (&(kte).keyblock)
+#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEY)
+# define kte_keyblock(kte) (&(kte).key)
+#else
+# error Cannot figure out how keytab entries work
+#endif
+#if HAVE_DECL_KRB5_FREE_KEYTAB_ENTRY_CONTENTS
+/* nothing */
+#elif HAVE_DECL_KRB5_KT_FREE_ENTRY
+# define krb5_free_keytab_entry_contents krb5_kt_free_entry
+#else
+static_inline int
+krb5_free_keytab_entry_contents(krb5_context ctx, krb5_keytab_entry *ent)
+{
+ krb5_free_principal(ctx, ent->principal);
+ krb5_free_keyblock_contents(ctx, kte_keyblock(ent));
+ return 0;
+}
+#endif
+#ifndef KRB5_KEYUSAGE_KDC_REP_TICKET
+# ifdef HAVE_DECL_KRB5_KU_TICKET
+# define KRB5_KEYUSAGE_KDC_REP_TICKET KRB5_KU_TICKET
+# else
+# define KRB5_KEYUSAGE_KDC_REP_TICKET 2
+# endif
+#endif
+
+static krb5_error_code
+reload_keys(void)
+{
+ krb5_error_code ret;
+ krb5_keytab fkeytab = NULL;
+ krb5_kt_cursor c;
+ krb5_keytab_entry kte;
+ int i, n_nkeys, o_nkeys;
+ krb5_keytab_entry *n_ktent = NULL, *o_ktent;
+
+ time(&last_reload);
+ if (keytab_name != NULL)
+ ret = krb5_kt_resolve(k5ctx, keytab_name, &fkeytab);
+ else
+ ret = krb5_kt_default(k5ctx, &fkeytab);
+ if (ret != 0)
+ goto cleanup;
+ ret = krb5_kt_start_seq_get(k5ctx, fkeytab, &c);
+ if (ret != 0)
+ goto cleanup;
+ n_nkeys = 0;
+ while (krb5_kt_next_entry(k5ctx, fkeytab, &kte, &c) == 0) {
+ krb5_free_keytab_entry_contents(k5ctx, &kte);
+ n_nkeys++;
+ }
+ krb5_kt_end_seq_get(k5ctx, fkeytab, &c);
+ if (n_nkeys == 0) {
+ ret = KRB5_KT_NOTFOUND;
+ goto cleanup;
+ }
+ n_ktent = calloc(n_nkeys, sizeof(krb5_keytab_entry));
+ if (n_ktent == NULL) {
+ ret = KRB5_KT_NOTFOUND;
+ goto cleanup;
+ }
+ ret = krb5_kt_start_seq_get(k5ctx, fkeytab, &c);
+ if (ret != 0)
+ goto cleanup;
+ for (i = 0; i < n_nkeys; i++)
+ if (krb5_kt_next_entry(k5ctx, fkeytab, &n_ktent[i], &c) != 0)
+ break;
+ krb5_kt_end_seq_get(k5ctx, fkeytab, &c);
+ if (i < n_nkeys)
+ goto cleanup;
+ have_keytab_keys = 1;
+ o_ktent = ktent;
+ ktent = n_ktent;
+
+ o_nkeys = nkeys;
+ nkeys = n_nkeys;
+
+ /* for cleanup */
+ n_ktent = o_ktent;
+ n_nkeys = o_nkeys;
+cleanup:
+ if (n_ktent != NULL) {
+ for (i = 0; i < n_nkeys; i++)
+ krb5_free_keytab_entry_contents(k5ctx, &n_ktent[i]);
+ free(n_ktent);
+ }
+ if (fkeytab != NULL) {
+ krb5_kt_close(k5ctx, fkeytab);
+ }
+ return ret;
+}
+
+#if defined(HAVE_KRB5_DECRYPT_TKT_PART) && !defined(HAVE_KRB5_C_DECRYPT)
+extern krb5_error_code
+encode_krb5_enc_tkt_part(krb5_enc_tkt_part *encpart, krb5_data **a_out);
+/*
+ * AIX krb5 has krb5_decrypt_tkt_part, but no krb5_c_decrypt. So, implement our
+ * own krb5_c_decrypt. Note that this krb5_c_decrypt is only suitable for
+ * decrypting an encrypted krb5_enc_tkt_part. But since that's all we ever use
+ * it for, that should be fine.
+ */
+static krb5_error_code
+krb5_c_decrypt(krb5_context context, const krb5_keyblock *key,
+ krb5_keyusage usage, const krb5_data *cipher_state,
+ const krb5_enc_data *input, krb5_data *output)
+{
+ krb5_ticket tkt;
+ krb5_error_code code;
+ krb5_data *tout;
+
+ osi_Assert(cipher_state == NULL);
+ osi_Assert(usage == KRB5_KEYUSAGE_KDC_REP_TICKET);
+
+ memset(&tkt, 0, sizeof(tkt));
+
+ tkt.enc_part = *input;
+
+ code = krb5_decrypt_tkt_part(context, key, &tkt);
+ if (code != 0)
+ return code;
+
+ code = encode_krb5_enc_tkt_part(tkt.enc_part2, &tout);
+ if (code != 0)
+ return code;
+
+ osi_Assert(tout->length <= output->length);
+
+ memcpy(output->data, tout->data, tout->length);
+ output->length = tout->length;
+
+ krb5_free_data(context, tout);
+ return 0;
+}
+#endif /* HAVE_KRB5_DECRYPT_TKT_PART && !HAVE_KRB5_C_DECRYPT */
+
+static int
+rxkad_keytab_decrypt(int kvno, int et, void *in, size_t inlen,
+ void *out, size_t *outlen)
+{
+ krb5_error_code code;
+ /* use heimdal api if available, since heimdal's interface to
+ krb5_c_decrypt is non-standard and annoying to use */
+#ifdef HAVE_KRB5_CRYPTO_INIT
+ krb5_crypto kcrypto;
+#else
+ krb5_enc_data ind;
+#endif
+ krb5_data outd;
+ int retried, i, foundkey;
+ MUTEX_ENTER(&krb5_lock);
+ if (have_keytab_keys == 0) {
+ if (time(NULL) - last_reload > 600) {
+ reload_keys();
+ }
+ if (have_keytab_keys == 0) {
+ MUTEX_EXIT(&krb5_lock);
+ return RXKADUNKNOWNKEY;
+ }
+ }
+ foundkey = 0;
+ code = -1;
+ retried = 0;
+retry:
+ for (i = 0; i < nkeys; i++) {
+ /* foundkey determines what error code we return for failure */
+ if (ktent[i].vno == kvno)
+ foundkey = 1;
+ /* but check against all keys if the enctype matches, for robustness */
+ if (kb_enctype(kte_keyblock(ktent[i])) == et) {
+#ifdef HAVE_KRB5_CRYPTO_INIT
+ code = krb5_crypto_init(k5ctx, kte_keyblock(ktent[i]), et,
+ &kcrypto);
+ if (code == 0) {
+ code = krb5_decrypt(k5ctx, kcrypto,
+ KRB5_KEYUSAGE_KDC_REP_TICKET, in, inlen,
+ &outd);
+ krb5_crypto_destroy(k5ctx, kcrypto);
+ }
+ if (code == 0) {
+ /* heimdal allocates new memory for the decrypted data; put
+ * the data back into the requested 'out' buffer */
+ osi_Assert(outd.length <= *outlen);
+ *outlen = outd.length;
+ memcpy(out, outd.data, outd.length);
+ krb5_data_free(&outd);
+ break;
+ }
+#else
+ outd.length = *outlen;
+ outd.data = out;
+ ind.ciphertext.length = inlen;
+ ind.ciphertext.data = in;
+ ind.enctype = et;
+ ind.kvno = kvno;
+ code = krb5_c_decrypt(k5ctx, kte_keyblock(ktent[i]),
+ KRB5_KEYUSAGE_KDC_REP_TICKET, NULL, &ind,
+ &outd);
+ if (code == 0) {
+ *outlen = outd.length;
+ break;
+ }
+#endif
+ }
+ }
+ if (code != 0 && time(NULL) - last_reload > 600 && !retried) {
+ reload_keys();
+ retried = 1;
+ goto retry;
+ }
+ MUTEX_EXIT(&krb5_lock);
+ if (code == 0)
+ return 0;
+ if (foundkey != 0)
+ return RXKADBADTICKET;
+ return RXKADUNKNOWNKEY;
+}
+
+#ifdef RX_ENABLE_LOCKS
+static int
+init_krb5_lock(void)
+{
+ MUTEX_INIT(&krb5_lock, "krb5 api", MUTEX_DEFAULT, 0);
+}
+
+static pthread_once_t rxkad_keytab_once_init = PTHREAD_ONCE_INIT;
+#define INIT_PTHREAD_LOCKS osi_Assert(pthread_once(&rxkad_keytab_once_init, init_krb5_lock)==0)
+#else
+#define INIT_PTHREAD_LOCKS
+#endif
+int
+rxkad_InitKeytabDecrypt(const char *ktname)
+{
+ int code;
+ static int keytab_init;
+ INIT_PTHREAD_LOCKS;
+ MUTEX_ENTER(&krb5_lock);
+ if (keytab_init) {
+ MUTEX_EXIT(&krb5_lock);
+ return 0;
+ }
+ k5ctx = NULL;
+ keytab_name = NULL;
+ code = krb5_init_context(&k5ctx);
+ if (code != 0)
+ goto cleanup;
+ if (ktname != NULL) {
+ keytab_name = strdup(ktname);
+ if (keytab_name == NULL) {
+ code = KRB5_KT_BADNAME;
+ goto cleanup;
+ }
+ }
+ keytab_init=1;
+ reload_keys();
+ MUTEX_EXIT(&krb5_lock);
+ return 0;
+cleanup:
+ if (keytab_name != NULL) {
+ free(keytab_name);
+ }
+ if (k5ctx != NULL) {
+ krb5_free_context(k5ctx);
+ }
+ MUTEX_EXIT(&krb5_lock);
+ return code;
+}
+
+int
+rxkad_BindKeytabDecrypt(struct rx_securityClass *aclass)
+{
+ return rxkad_SetAltDecryptProc(aclass, rxkad_keytab_decrypt);
+}