]> git.michaelhowe.org Git - packages/o/openafs.git/commitdiff
Clean up akimpersonate and use for server-to-server
authorBen Kaduk <kaduk@mit.edu>
Tue, 14 May 2013 23:42:05 +0000 (19:42 -0400)
committerRuss Allbery <rra@debian.org>
Mon, 22 Jul 2013 22:26:03 +0000 (15:26 -0700)
Since a6d7cacfd, aklog has been able to print a krb5 ticket to
itself for an arbitrary client principal, allowing a user with
access to the cell's krb5 key to get tokens as an arbitrary user.

Now that it is possible to use native krb5 tickets with non-DES
enctypes for authentication, and akimpersonate is available from libauth,
use printed native krb5 tickets for server-to-server communication (as well
as the -localauth versions of the client utilities).

Before doing so, refactor the akimpersonate code to be more usable
and readable, and eliminate some dead code.  For example, we always printed
addressless tickets, so that code could be removed.  Other code had excessive
stack usage for a library routine, which is eliminated.  Use a start time
of 0 instead of 300 so that the printed ticket will always be
detected as infinite-lifetime.

In order to ensure usability on all platforms (in particular Solaris),
provide a couple more compat shims to implement routines which are not
always available from the krb5 library, in particular encode_krb5_ticket
and encode_krb5_enc_tkt_part.  Thanks to Andrew Deason for implementing
these compatibility routines.

UKERNEL doesn't need this stuff.

akimpersonate: Handle missing encode_krb5_ticket

If we don't have encode_krb5_ticket and encode_krb5_enc_tkt_part,
implement our own. Basically, transform the appropriate structure into
the Heimdal equivalent, and use our builtin Heimdal ASN.1 encoding
functions to do the actual encoding for us.

src/aklog/aklog_main.c
src/auth/Makefile.in
src/auth/akimpersonate.c
src/auth/akimpersonate_v5gen.c [new file with mode: 0644]
src/auth/akimpersonate_v5gen.h [new file with mode: 0644]
src/auth/authcon.c
src/libafsauthent/Makefile.in
src/shlibafsauthent/Makefile.in

index 1d9c9058d5a13e84d18e838fe0f3eed80a55cdd5..01832df35a2dffaab02805c6dfb3e8eb69a14ded 100644 (file)
@@ -1503,17 +1503,12 @@ static krb5_error_code get_credv5(krb5_context context,
        get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
     
     if (keytab) {
-       int allowed_enctypes[] = {
-           ENCTYPE_DES_CBC_CRC, 0
-       };
-
        r = get_credv5_akimpersonate(context,
                                     keytab,
                                     increds.server,
                                     increds.client,
-                                    300, ((~0U)>>1),
-                                    allowed_enctypes,
-                                    0 /* paddress */,
+                                    0, 0x7fffffff,
+                                    NULL,
                                     creds /* out */);
     } else {
        r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
index a94aa6fb8bf24cb6585f2208aa6a762b0e879c81..c259c692eb53ccea55d45ecb231503d192abe226 100644 (file)
@@ -9,15 +9,15 @@ srcdir=@srcdir@
 include @TOP_OBJDIR@/src/config/Makefile.config
 
 OBJS= cellconfig.o ktc.o userok.o writeconfig.o authcon.o \
-    acfg_errors.o ktc_errors.o @MAKE_KRB5@ akimpersonate.o
+    acfg_errors.o ktc_errors.o @MAKE_KRB5@ akimpersonate.o akimpersonate_v5gen.o
 KOBJS= cellconfig.o ktc.krb.o userok.o writeconfig.o authcon.o \
-    acfg_errors.o ktc_errors.o @MAKE_KRB5@ akimpersonate.o
+    acfg_errors.o ktc_errors.o @MAKE_KRB5@ akimpersonate.o akimpersonate_v5gen.o
 
 LIBS=libauth.a \
       ${TOP_LIBDIR}/librxkad.a ${TOP_LIBDIR}/libdes.a \
       ${TOP_LIBDIR}/librx.a ${TOP_LIBDIR}/libsys.a \
       ${TOP_LIBDIR}/liblwp.a ${TOP_LIBDIR}/util.a
-INCLS=cellconfig.h auth.h keys.h akimpersonate.h
+INCLS=cellconfig.h auth.h keys.h akimpersonate.h akimpersonate_v5gen.h
 KSRCS=auth.h
 UKSRCS=${KSRCS} cellconfig.h acfg_errors.c keys.h cellconfig.c \
        ktc.c authcon.c ktc_errors.c
@@ -44,6 +44,9 @@ authcon.o: authcon.c ${INCLS}
 akimpersonate.o: akimpersonate.c ${INCLS}
        ${CCOBJ} ${CFLAGS} -c ${srcdir}/akimpersonate.c @KRB5CFLAGS@
 
+akimpersonate_v5gen.o: akimpersonate_v5gen.c ${INCLS}
+       ${CCOBJ} ${CFLAGS} -c ${srcdir}/akimpersonate_v5gen.c @KRB5CFLAGS@ -I${srcdir}/../rxkad
+
 userok.o: userok.c ${INCLS}
 cellconfig.o: cellconfig.c ${INCLS}
 copyauth.o: copyauth.c ${INCLS} AFS_component_version_number.o
index dc2f009bbdd2bf86102118982a92136d6ac58024..44c6e6f4474b75c237b85a8b483c55d5ab0924f9 100644 (file)
  * if it has been or is hereafter advised of the possibility of
  * such damages.
  */
+/*
+ * Copyright (C) 2013 by Alexander Chernyakhovsky and the
+ * Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #include <afsconfig.h>
 #include <afs/param.h>
@@ -38,6 +68,7 @@
 #include <krb5.h>
 
 #include "akimpersonate.h"
+#include "akimpersonate_v5gen.h"
 
 #ifdef HAVE_KRB5_CREDS_KEYBLOCK
 #define USING_MIT 1
 #endif
 
 #if USING_HEIMDAL
-#define deref_keyblock_enctype(kb)             \
-    ((kb)->keytype)
+#define deref_keyblock_enctype(kb)     ((kb)->keytype)
+#define deref_entry_keyblock(entry)    ((entry)->keyblock)
+#define deref_session_key(creds)       ((creds)->session)
+#define deref_enc_tkt_addrs(tkt)       ((tkt)->caddr)
+#define deref_enc_data(enc)            ((enc)->cipher.data)
+#else
+#define deref_keyblock_enctype(kb)     ((kb)->enctype)
+#define deref_entry_keyblock(entry)    ((entry)->key)
+#define deref_session_key(creds)       ((creds)->keyblock)
+#define deref_enc_tkt_addrs(tkt)       ((tkt)->caddrs)
+#define deref_enc_data(enc)            ((enc)->ciphertext.data)
+#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
 
-#define deref_entry_keyblock(entry)            \
-    entry->keyblock
+#define deref_entry_enctype(entry)                     \
+    deref_keyblock_enctype(&deref_entry_keyblock(entry))
 
-#define deref_session_key(creds)               \
-    creds->session
+#ifdef USING_MIT
+# if !defined(HAVE_ENCODE_KRB5_TICKET)
+/*
+ * Solaris doesn't have encode_krb5_ticket and encode_krb5_enc_tkt_part, so we
+ * need to implement our own. The akv5gen_* functions below are implemented
+ * using v5gen code; so, they need to have no krb5 structures in their
+ * arguments, since using system krb5 headers at the same time as v5gen
+ * headers is problematic. That's why the ticket contents are exploded.
+ */
+static krb5_error_code
+encode_krb5_ticket(krb5_ticket *rep, krb5_data **a_out)
+{
+    krb5_error_code code = 0;
+    int i;
+    char **names = NULL;
+    krb5_data *out = NULL;
+    size_t out_len = 0;
+    char *out_data = NULL;
 
-#define deref_enc_tkt_addrs(tkt)               \
-    tkt->caddr
+    *a_out = NULL;
 
-#define deref_enc_length(enc)                  \
-    ((enc)->cipher.length)
+    out = calloc(1, sizeof(*out));
+    if (!out) {
+       code = ENOMEM;
+       goto cleanup;
+    }
 
-#define deref_enc_data(enc)                    \
-    ((enc)->cipher.data)
+    names = calloc(rep->server->length, sizeof(names[0]));
+    if (names == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
 
-#define krb5_free_keytab_entry_contents krb5_kt_free_entry
+    for (i = 0; i < rep->server->length; i++) {
+       names[i] = rep->server->data[i].data;
+    }
 
-#else
-#define deref_keyblock_enctype(kb)             \
-    ((kb)->enctype)
+    code = akv5gen_encode_krb5_ticket(rep->enc_part.kvno,
+                                      rep->server->realm.data,
+                                      rep->server->type,
+                                      rep->server->length,
+                                      names,
+                                      rep->enc_part.enctype,
+                                      rep->enc_part.ciphertext.length,
+                                      rep->enc_part.ciphertext.data,
+                                      &out_len,
+                                      &out_data);
+    if (code != 0) {
+       goto cleanup;
+    }
+
+    out->length = out_len;
+    out->data = out_data;
+    *a_out = out;
+    out = NULL;
 
-#define deref_entry_keyblock(entry)            \
-    entry->key
+ cleanup:
+    free(names);
+    free(out);
+    return code;
+}
+# endif /* !HAVE_ENCODE_KRB5_TICKET */
+
+# if !defined(HAVE_ENCODE_KRB5_ENC_TKT_PART)
+static krb5_error_code
+encode_krb5_enc_tkt_part(krb5_enc_tkt_part *encpart, krb5_data **a_out)
+{
+    krb5_error_code code = 0;
+    int i;
+    char **names = NULL;
+    krb5_data *out = NULL;
+    size_t out_len = 0;
+    char *out_data = NULL;
 
-#define deref_session_key(creds)               \
-    creds->keyblock
+    *a_out = NULL;
 
-#define deref_enc_tkt_addrs(tkt)               \
-    tkt->caddrs
+    out = calloc(1, sizeof(*out));
+    if (out == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
 
-#define deref_enc_length(enc)                  \
-    ((enc)->ciphertext.length)
+    names = calloc(encpart->client->length, sizeof(names[0]));
+    if (names == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
 
-#define deref_enc_data(enc)                    \
-    ((enc)->ciphertext.data)
+    for (i = 0; i < encpart->client->length; i++)
+       names[i] = encpart->client->data[i].data;
 
-#endif
+    if (encpart->flags != TKT_FLG_INITIAL) {
+       /* We assume the ticket has the flag _INITIAL set, and only that flag.
+        * passing each individual flag to akv5gen would be really ugly, and
+        * should be unnecessary. */
+       goto invalid;
+    }
+    if (encpart->caddrs != NULL && encpart->caddrs[0] != NULL)
+       goto invalid;
+    if (encpart->authorization_data && encpart->authorization_data[0])
+       goto invalid;
+
+    code = akv5gen_encode_krb5_enc_tkt_part(encpart->session->enctype,
+                                            encpart->session->length,
+                                            encpart->session->contents,
+                                            encpart->client->realm.data,
+                                            encpart->client->type,
+                                            encpart->client->length,
+                                            names,
+                                            encpart->transited.tr_type,
+                                            encpart->transited.tr_contents.length,
+                                            encpart->transited.tr_contents.data,
+                                            encpart->times.authtime,
+                                            encpart->times.starttime,
+                                            encpart->times.endtime,
+                                            encpart->times.renew_till,
+                                            &out_len,
+                                            &out_data);
+    if (code != 0)
+       goto cleanup;
 
-#define deref_entry_enctype(entry)                     \
-    deref_keyblock_enctype(&deref_entry_keyblock(entry))
+    out->length = out_len;
+    out->data = out_data;
+    *a_out = out;
+    out = NULL;
 
-#if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT)
+ cleanup:
+    free(names);
+    free(out);
+    return code;
+
+ invalid:
+    /* XXX or assert, or ...? */
+    code = EINVAL;
+    goto cleanup;
+}
+# endif /* !HAVE_ENCODE_KRB5_ENC_TKT_PART */
+
+# if !defined(HAVE_KRB5_ENCRYPT_TKT_PART)
 krb5_error_code
 krb5_encrypt_tkt_part(krb5_context context,
                      const krb5_keyblock *key,
@@ -124,248 +278,349 @@ Done:
     }
     return code;
 }
+# endif /* HAVE_KRB5_ENCRYPT_TKT_PART */
+#endif /* USING_MIT */
+
+static const int any_enctype[2] = {0, 0};
+static const krb5_data empty_string;
+
+/*
+ * Routines to allocate/free the extra storage involved in a ticket structure.
+ * When changing one, ensure that the other is changed to reflect the
+ * allocation contract.
+ */
+static int
+alloc_ticket(void **out)
+{
+#if USING_HEIMDAL
+    Ticket *ticket_reply;
+#else
+    krb5_ticket *ticket_reply;
 #endif
 
-krb5_error_code get_credv5_akimpersonate(krb5_context context,
-                                        char* keytab,
-                                        krb5_principal service_principal,
-                                        krb5_principal client_principal,
-                                        time_t starttime,
-                                        time_t endtime,
-                                        int *allowed_enctypes,
-                                        int *paddress,
-                                        krb5_creds** out_creds /* out */ )
+    /* requisite aliasing for MIT/Heimdal support. */
+    ticket_reply = *out = calloc(1, sizeof(*ticket_reply));
+    if (ticket_reply == NULL)
+       return ENOMEM;
+
+#if USING_HEIMDAL
+    ticket_reply->enc_part.kvno = malloc(sizeof(*ticket_reply->enc_part.kvno));
+    if (ticket_reply->enc_part.kvno == NULL)
+       return ENOMEM;
+#else
+    /* No allocations needed for MIT's krb5_ticket structure. */
+#endif
+    return 0;
+}
+
+static void
+free_ticket(void *in)
 {
-#if defined(USING_HEIMDAL) || (defined(HAVE_ENCODE_KRB5_ENC_TKT) && defined(HAVE_ENCODE_KRB5_TICKET) && defined(HAVE_KRB5_C_ENCRYPT))
-    krb5_error_code code;
-    krb5_keytab kt = 0;
-    krb5_kt_cursor cursor[1];
-    krb5_keytab_entry entry[1];
-    krb5_ccache cc = 0;
-    krb5_creds *creds = 0;
-    krb5_enctype enctype;
-    krb5_kvno kvno;
-    krb5_keyblock session_key[1];
 #if USING_HEIMDAL
-    Ticket ticket_reply[1];
-    EncTicketPart enc_tkt_reply[1];
-    krb5_address address[30];
-    krb5_addresses faddr[1];
-    int temp_vno[1];
-    time_t temp_time[2];
+    Ticket *ticket_reply;
 #else
-    krb5_ticket ticket_reply[1];
-    krb5_enc_tkt_part enc_tkt_reply[1];
-    krb5_address address[30], *faddr[30];
+    krb5_ticket *ticket_reply;
 #endif
-    krb5_data * temp;
-    int i;
-    static int any_enctype[] = {0};
-    *out_creds = 0;
-    if (!(creds = malloc(sizeof *creds))) {
-        code = ENOMEM;
-        goto cleanup;
-    }
-    if (!allowed_enctypes)
-        allowed_enctypes = any_enctype;
 
-    cc = 0;
-    enctype = 0; /* AKIMPERSONATE_IGNORE_ENCTYPE */
-    kvno = 0; /* AKIMPERSONATE_IGNORE_VNO */
-    memset((char*)creds, 0, sizeof *creds);
-    memset((char*)entry, 0, sizeof *entry);
-    memset((char*)session_key, 0, sizeof *session_key);
-    memset((char*)ticket_reply, 0, sizeof *ticket_reply);
-    memset((char*)enc_tkt_reply, 0, sizeof *enc_tkt_reply);
-    code = krb5_kt_resolve(context, keytab, &kt);
-    if (code) {
-        goto cleanup;
+    /* requisite aliasing for MIT/Heimdal support. */
+    ticket_reply = in;
+    if (ticket_reply == NULL)
+       return;
+
+#if USING_HEIMDAL
+    if (ticket_reply->enc_part.kvno != NULL)
+       free(ticket_reply->enc_part.kvno);
+#else
+    /* No allocations needed for MIT's krb5_ticket structure. */
+#endif
+    free(ticket_reply);
+}
+
+/*
+ * Routines to allocate/free the extra storage involved in an encrypted
+ * ticket part structure.
+ * When changing one, ensure that the other is changed to reflect the
+ * allocation contract.
+ */
+static int
+alloc_enc_tkt_part(void **out)
+{
+#if USING_HEIMDAL
+    EncTicketPart *enc_tkt_reply;
+#else
+    krb5_enc_tkt_part *enc_tkt_reply;
+#endif
+
+    /* Aliasing for MIT/Heimdal support. */
+    enc_tkt_reply = *out = calloc(1, sizeof(*enc_tkt_reply));
+    if (enc_tkt_reply == NULL)
+       return ENOMEM;
+
+#if USING_HEIMDAL
+    enc_tkt_reply->starttime = malloc(sizeof(*enc_tkt_reply->starttime));
+    if (enc_tkt_reply->starttime == NULL)
+       return ENOMEM;
+#else
+    /* No allocations needed for MIT's krb5_enc_tkt_part structure. */
+#endif
+    return 0;
+}
+static void
+free_enc_tkt_part(void *in)
+{
+#if USING_HEIMDAL
+    EncTicketPart *enc_tkt_reply;
+#else
+    krb5_enc_tkt_part *enc_tkt_reply;
+#endif
+
+    /* Aliasing for MIT/Heimdal support. */
+    enc_tkt_reply = in;
+    if (enc_tkt_reply == NULL)
+       return;
+
+#if USING_HEIMDAL
+    if (enc_tkt_reply->starttime != NULL)
+       free(enc_tkt_reply->starttime);
+#else
+    /* No allocations needed for MIT's krb5_enc_tkt_part structure. */
+#endif
+    free(enc_tkt_reply);
+}
+
+/*
+ * Given a keytab, extract the principal name of the (first) entry with
+ * the highest kvno in the keytab.  This provides compatibility with the
+ * rxkad KeyFile behavior of always using the highest kvno entry when
+ * printing tickets.  We could return the kvno as well, but krb5_kt_get_entry
+ * can find the highest kvno on its own.
+ *
+ * Returns 0 on success, krb5 errors on failure.
+ */
+static int
+pick_principal(krb5_context context, krb5_keytab kt,
+              krb5_principal *service_principal)
+{
+    krb5_error_code code;
+    krb5_kvno vno = 0;
+    krb5_kt_cursor c;
+    krb5_keytab_entry n_entry;
+
+    /* Nothing to do */
+    if (*service_principal != NULL)
+       return 0;
+
+    memset(&n_entry, 0, sizeof(n_entry));
+
+    code = krb5_kt_start_seq_get(context, kt, &c);
+    if (code != 0)
+       goto cleanup;
+    while (code == 0 && krb5_kt_next_entry(context, kt, &n_entry, &c) == 0) {
+       if (n_entry.vno > vno) {
+           vno = n_entry.vno;
+           (void)krb5_free_principal(context, *service_principal);
+           code = krb5_copy_principal(context, n_entry.principal,
+                                      service_principal);
+       }
+       (void)krb5_free_keytab_entry_contents(context, &n_entry);
     }
+    if (code != 0) {
+       (void)krb5_kt_end_seq_get(context, kt, &c);
+       goto cleanup;
+    }
+    code = krb5_kt_end_seq_get(context, kt, &c);
 
-    if (service_principal) {
-        for (i = 0; (enctype = allowed_enctypes[i]) || !i; ++i) {
-           code = krb5_kt_get_entry(context,
-                                    kt,
-                                    service_principal,
-                                    kvno,
-                                    enctype,
-                                    entry);
-           if (!code) {
-               if (allowed_enctypes[i])
-                   deref_keyblock_enctype(session_key) = allowed_enctypes[i];
-               break;
-           }
-        }
-        if (code) {
+cleanup:
+    return code;
+}
+
+/*
+ * Given a keytab and a list of allowed enctypes, and optionally a known
+ * service principal, choose an appropriate enctype, and choose a
+ * service principal if one was not given.  Return the keytab entry
+ * corresponding to this service principal and enctype.
+ *
+ * The list of allowed enctypes must be zero-terminated.
+ */
+static int
+pick_enctype_and_principal(krb5_context context, krb5_keytab kt,
+                          const int *allowed_enctypes, krb5_enctype *enctype,
+                          krb5_principal *service_principal,
+                          krb5_keytab_entry *entry)
+{
+    krb5_error_code code;
+    int i;
+
+    if (*service_principal == NULL) {
+       code = pick_principal(context, kt, service_principal);
+       if (code != 0) {
            goto cleanup;
-        }
-    } else {
-        krb5_keytab_entry new[1];
-        int best = -1;
-        memset(new, 0, sizeof *new);
-        if ((code == krb5_kt_start_seq_get(context, kt, cursor))) {
-            goto cleanup;
-        }
-        while (!(code = krb5_kt_next_entry(context, kt, new, cursor))) {
-            for (i = 0;
-                    allowed_enctypes[i] && allowed_enctypes[i]
-                    != deref_entry_enctype(new); ++i)
-                ;
-            if ((!i || allowed_enctypes[i]) &&
-               (best < 0 || best > i)) {
-                krb5_free_keytab_entry_contents(context, entry);
-                *entry = *new;
-                memset(new, 0, sizeof *new);
-            } else krb5_free_keytab_entry_contents(context, new);
-        }
-        if ((i = krb5_kt_end_seq_get(context, kt, cursor))) {
-            code = i;
-            goto cleanup;
-        }
-        if (best < 0) {
-            goto cleanup;
-        }
-        deref_keyblock_enctype(session_key) = deref_entry_enctype(entry);
+       }
     }
 
-    /* Make Ticket */
+    /* We always have a service_principal, now. */
+    i = 0;
+    do {
+       *enctype = allowed_enctypes[i];
+       code = krb5_kt_get_entry(context, kt, *service_principal, 0 /* any */,
+                                *enctype, entry);
+       if (code == 0) {
+           if (*enctype == 0)
+               *enctype = deref_entry_enctype(entry);
+           break;
+       }
+       ++i;
+    } while(allowed_enctypes[i] != 0);
+    if (code != 0)
+       goto cleanup;
+
+cleanup:
+    return code;
+}
+
+/*
+ * Populate the encrypted part of the ticket.
+ */
+static void
+populate_enc_tkt(krb5_keyblock *session_key, krb5_principal client_principal,
+                time_t starttime, time_t endtime, void *out)
+{
+#if USING_HEIMDAL
+    EncTicketPart *enc_tkt_reply;
+#else
+    krb5_enc_tkt_part *enc_tkt_reply;
+#endif
+    int i;
+
+    /* Alias through void* since Heimdal and MIT's types differ. */
+    enc_tkt_reply = out;
 
 #if USING_HEIMDAL
-    if ((code = krb5_generate_random_keyblock(context,
-                                             deref_keyblock_enctype(session_key), session_key))) {
-        goto cleanup;
-    }
     enc_tkt_reply->flags.initial = 1;
     enc_tkt_reply->transited.tr_type = DOMAIN_X500_COMPRESS;
     enc_tkt_reply->cname = client_principal->name;
     enc_tkt_reply->crealm = client_principal->realm;
     enc_tkt_reply->key = *session_key;
-    {
-        static krb5_data empty_string;
-        enc_tkt_reply->transited.contents = empty_string;
-    }
+    enc_tkt_reply->transited.contents = empty_string;
     enc_tkt_reply->authtime = starttime;
-    enc_tkt_reply->starttime = temp_time;
     *enc_tkt_reply->starttime = starttime;
-#if 0
-    enc_tkt_reply->renew_till = temp_time + 1;
-    *enc_tkt_reply->renew_till = endtime;
-#endif
     enc_tkt_reply->endtime = endtime;
 #else
-    if ((code = krb5_c_make_random_key(context,
-                                      deref_keyblock_enctype(session_key), session_key))) {
-        goto cleanup;
-    }
     enc_tkt_reply->magic = KV5M_ENC_TKT_PART;
-#define DATACAST        (unsigned char *)
     enc_tkt_reply->flags |= TKT_FLG_INITIAL;
     enc_tkt_reply->transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
     enc_tkt_reply->session = session_key;
     enc_tkt_reply->client = client_principal;
-    {
-        static krb5_data empty_string;
-        enc_tkt_reply->transited.tr_contents = empty_string;
-    }
+    enc_tkt_reply->transited.tr_contents = empty_string;
     enc_tkt_reply->times.authtime = starttime;
     enc_tkt_reply->times.starttime = starttime; /* krb524init needs this */
     enc_tkt_reply->times.endtime = endtime;
 #endif  /* USING_HEIMDAL */
-    /* NB:  We will discard address for now--ignoring caddr field
-       in any case.  MIT branch does what it always did. */
+}
 
-    if (paddress && *paddress) {
-        deref_enc_tkt_addrs(enc_tkt_reply) = faddr;
-#if USING_HEIMDAL
-        faddr->len = 0;
-        faddr->val = address;
-#endif
-        for (i = 0; paddress[i]; ++i) {
-#if USING_HEIMDAL
-            address[i].addr_type = KRB5_ADDRESS_INET;
-            address[i].address.data = (void*)(paddress+i);
-            address[i].address.length = sizeof(paddress[i]);
-#else
-#if !USING_SSL
-            address[i].magic = KV5M_ADDRESS;
-            address[i].addrtype = ADDRTYPE_INET;
-#else
-            address[i].addrtype = AF_INET;
-#endif
-            address[i].contents = (void*)(paddress+i);
-            address[i].length = sizeof(int);
-            faddr[i] = address+i;
-#endif
-        }
+/*
+ * Encrypt the provided enc_tkt_part structure with the key from the keytab
+ * entry entry, and place the resulting blob in the ticket_reply structure.
+ */
+static int
+encrypt_enc_tkt(krb5_context context, krb5_principal service_principal,
+               krb5_keytab_entry *entry, void *tr_out, void *er_in)
+{
+    krb5_error_code code;
 #if USING_HEIMDAL
-        faddr->len = i;
+    Ticket *ticket_reply;
+    EncTicketPart *enc_tkt_reply;
+    krb5_crypto crypto = 0;
+    unsigned char *buf = 0;
+    size_t buf_size, buf_len;
 #else
-        faddr[i] = 0;
+    krb5_ticket *ticket_reply;
+    krb5_enc_tkt_part *enc_tkt_reply;
 #endif
-    }
+
+    /* Requisite aliasing for Heimdal/MIT support. */
+    ticket_reply = tr_out;
+    enc_tkt_reply = er_in;
 
 #if USING_HEIMDAL
     ticket_reply->sname = service_principal->name;
     ticket_reply->realm = service_principal->realm;
 
-    { /* crypto block */
-        krb5_crypto crypto = 0;
-        unsigned char *buf = 0;
-        size_t buf_size, buf_len;
-        char *what;
-
-        ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size,
-                          enc_tkt_reply, &buf_len, code);
-        if(code) {
-            goto cleanup;
-        }
-
-        if(buf_len != buf_size) {
-            goto cleanup;
-        }
-        what = "krb5_crypto_init";
-        code = krb5_crypto_init(context,
-                               &deref_entry_keyblock(entry),
-                               deref_entry_enctype(entry),
-                               &crypto);
-        if(!code) {
-            what = "krb5_encrypt";
-            code = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET,
-                                             buf, buf_len, entry->vno, &(ticket_reply->enc_part));
-        }
-        if (buf) free(buf);
-        if (crypto) krb5_crypto_destroy(context, crypto);
-        if(code) {
-            goto cleanup;
-        }
-    } /* crypto block */
+    ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, enc_tkt_reply,
+                      &buf_len, code);
+    if (code != 0)
+       goto cleanup;
+
+    if (buf_len != buf_size)
+       goto cleanup;
+    code = krb5_crypto_init(context,
+                           &deref_entry_keyblock(entry),
+                           deref_entry_enctype(entry),
+                           &crypto);
+    if (code != 0)
+       goto cleanup;
+    code = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_TICKET, buf,
+                                     buf_len, entry->vno,
+                                     &(ticket_reply->enc_part));
+    if (code != 0)
+       goto cleanup;
     ticket_reply->enc_part.etype = deref_entry_enctype(entry);
-    ticket_reply->enc_part.kvno = temp_vno;
     *ticket_reply->enc_part.kvno = entry->vno;
     ticket_reply->tkt_vno = 5;
 #else
     ticket_reply->server = service_principal;
     ticket_reply->enc_part2 = enc_tkt_reply;
-    if ((code = krb5_encrypt_tkt_part(context, &deref_entry_keyblock(entry), ticket_reply))) {
+    code = krb5_encrypt_tkt_part(context, &deref_entry_keyblock(entry),
+                                ticket_reply);
+    if (code != 0)
         goto cleanup;
-    }
     ticket_reply->enc_part.kvno = entry->vno;
 #endif
 
-    /* Construct Creds */
+cleanup:
+#if USING_HEIMDAL
+    if (buf != NULL)
+       free(buf);
+    if (crypto != NULL)
+       krb5_crypto_destroy(context, crypto);
+#endif
+    return code;
+}
+
+/*
+ * Populate the credentials structure corresponding to the ticket we are
+ * printing.
+ */
+static int
+populate_creds(krb5_context context, krb5_principal service_principal,
+              krb5_principal client_principal, krb5_keyblock *session_key,
+              void *tr_in, void *er_in, krb5_creds *creds)
+{
+    krb5_error_code code;
+#if USING_HEIMDAL
+    Ticket *ticket_reply;
+    EncTicketPart *enc_tkt_reply;
+    size_t dummy;
+#else
+    krb5_ticket *ticket_reply;
+    krb5_enc_tkt_part *enc_tkt_reply;
+    krb5_data *temp = NULL;
+#endif
+
+    /* Requisite aliasing for Heimdal/MIT support. */
+    ticket_reply = tr_in;
+    enc_tkt_reply = er_in;
 
-    if ((code = krb5_copy_principal(context, service_principal,
-                                   &creds->server))) {
+    code = krb5_copy_principal(context, service_principal, &creds->server);
+    if (code != 0)
         goto cleanup;
-    }
-    if ((code = krb5_copy_principal(context, client_principal,
-                                   &creds->client))) {
+    code = krb5_copy_principal(context, client_principal, &creds->client);
+    if (code != 0)
         goto cleanup;
-    }
-    if ((code = krb5_copy_keyblock_contents(context, session_key,
-                                           &deref_session_key(creds)))) {
+    code = krb5_copy_keyblock_contents(context, session_key,
+                                      &deref_session_key(creds));
+    if (code != 0)
         goto cleanup;
-    }
 
 #if USING_HEIMDAL
     creds->times.authtime = enc_tkt_reply->authtime;
@@ -377,49 +632,134 @@ krb5_error_code get_credv5_akimpersonate(krb5_context context,
     creds->times = enc_tkt_reply->times;
     creds->ticket_flags = enc_tkt_reply->flags;
 #endif
-    if (!deref_enc_tkt_addrs(enc_tkt_reply))
-        ;
-    else if ((code = krb5_copy_addresses(context,
-                                        deref_enc_tkt_addrs(enc_tkt_reply), &creds->addresses))) {
-        goto cleanup;
-    }
 
 #if USING_HEIMDAL
-    {
-       size_t creds_tkt_len;
-       ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
-                          ticket_reply, &creds_tkt_len, code);
-       if(code) {
-           goto cleanup;
-       }
-    }
+    ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+                      ticket_reply, &dummy, code);
+    if (code != 0 || dummy != creds->ticket.length)
+       goto cleanup;
 #else
-    if ((code = encode_krb5_ticket(ticket_reply, &temp))) {
+    code = encode_krb5_ticket(ticket_reply, &temp);
+    if (code != 0)
        goto cleanup;
-    }
     creds->ticket = *temp;
+#endif
+
+cleanup:
+#if USING_HEIMDAL
+    /* nothing */
+#else
     free(temp);
 #endif
+    return code;
+}
+
+/*
+ * Print a krb5 ticket in our service key, for the supplied client principal.
+ * The path to a keytab is mandatory, but the service principal may be
+ * guessed from the keytab contents if desired.  The keytab entry must be
+ * one of the allowed_enctypes (a zero-terminated list) if a non-NULL
+ * parameter is passed.
+ */
+krb5_error_code
+get_credv5_akimpersonate(krb5_context context, char* keytab,
+                        krb5_principal service_principal,
+                        krb5_principal client_principal, time_t starttime,
+                        time_t endtime, const int *allowed_enctypes,
+                        krb5_creds** out_creds /* out */ )
+{
+    krb5_error_code code;
+    krb5_keytab kt = 0;
+    krb5_keytab_entry entry[1];
+    krb5_creds *creds = 0;
+    krb5_enctype enctype;
+    krb5_keyblock session_key[1];
+#if USING_HEIMDAL
+    Ticket *ticket_reply;
+    EncTicketPart *enc_tkt_reply;
+#else
+    krb5_ticket *ticket_reply;
+    krb5_enc_tkt_part *enc_tkt_reply;
+#endif
+    int i;
+    *out_creds = NULL;
+    enctype = 0; /* AKIMPERSONATE_IGNORE_ENCTYPE */
+    memset(entry, 0, sizeof *entry);
+    memset(session_key, 0, sizeof *session_key);
+    ticket_reply = NULL;
+    enc_tkt_reply = NULL;
+
+    creds = calloc(1, sizeof(*creds));
+    if (creds == NULL) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+    code = alloc_ticket(&ticket_reply);
+    if (code != 0)
+       goto cleanup;
+    code = alloc_enc_tkt_part(&enc_tkt_reply);
+    if (code != 0)
+       goto cleanup;
+    /* Empty list of allowed etypes must fail.  Do it here to avoid issues. */
+    if (allowed_enctypes != NULL && *allowed_enctypes == 0) {
+       code = KRB5_BAD_ENCTYPE;
+       goto cleanup;
+    }
+    if (allowed_enctypes == NULL)
+        allowed_enctypes = any_enctype;
+
+    if (keytab != NULL)
+      code = krb5_kt_resolve(context, keytab, &kt);
+    else
+      code = krb5_kt_default(context, &kt);
+    if (code != 0)
+        goto cleanup;
+
+    code = pick_enctype_and_principal(context, kt, allowed_enctypes,
+                                     &enctype, &service_principal, entry);
+    if (code != 0)
+       goto cleanup;
+
+    /* Conjure up a random session key */
+    deref_keyblock_enctype(session_key) = enctype;
+#if USING_HEIMDAL
+    code = krb5_generate_random_keyblock(context, enctype, session_key);
+#else
+    code = krb5_c_make_random_key(context, enctype, session_key);
+#endif
+    if (code != 0)
+        goto cleanup;
+
+    populate_enc_tkt(session_key, client_principal, starttime, endtime,
+                    enc_tkt_reply);
+
+    code = encrypt_enc_tkt(context, service_principal, entry, ticket_reply,
+                          enc_tkt_reply);
+    if (code != 0)
+       goto cleanup;
+
+    code = populate_creds(context, service_principal, client_principal,
+                         session_key, ticket_reply, enc_tkt_reply, creds);
+    if (code != 0)
+       goto cleanup;
+
     /* return creds */
     *out_creds = creds;
-    creds = 0;
+    creds = NULL;
 cleanup:
-    if (deref_enc_data(&ticket_reply->enc_part))
+    if (deref_enc_data(&ticket_reply->enc_part) != NULL)
         free(deref_enc_data(&ticket_reply->enc_part));
     krb5_free_keytab_entry_contents(context, entry);
-    if (client_principal)
+    if (client_principal != NULL)
         krb5_free_principal(context, client_principal);
-    if (service_principal)
+    if (service_principal != NULL)
         krb5_free_principal(context, service_principal);
-    if (cc)
-        krb5_cc_close(context, cc);
-    if (kt)
+    if (kt != NULL)
         krb5_kt_close(context, kt);
-    if (creds) krb5_free_creds(context, creds);
+    if (creds != NULL)
+       krb5_free_creds(context, creds);
     krb5_free_keyblock_contents(context, session_key);
-out:
+    free_ticket(ticket_reply);
+    free_enc_tkt_part(enc_tkt_reply);
     return code;
-#else
-    return -1;
-#endif
 }
diff --git a/src/auth/akimpersonate_v5gen.c b/src/auth/akimpersonate_v5gen.c
new file mode 100644 (file)
index 0000000..623b551
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2013 Sine Nomine Associates
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ *
+ * * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+/* are we using MIT krb5, and are we missing the functions encode_krb5_ticket
+ * and encode_krb5_enc_tkt_part? */
+#if defined(HAVE_KRB5_CREDS_KEYBLOCK) && !defined(HAVE_KRB5_CREDS_SESSION) \
+    && !defined(HAVE_ENCODE_KRB5_TICKET) && !defined(HAVE_ENCODE_KRB5_ENC_TKT_PART)
+
+# include <afs/stds.h>
+
+# include <sys/types.h>
+# include <time.h>
+# include <errno.h>
+# include <netinet/in.h>
+# include <string.h>
+# include <rx/xdr.h>
+# include <rx/rx.h>
+# include <des.h>
+# include <des_prototypes.h>
+
+# include "lifetimes.h"
+# include "rxkad.h"
+
+# include "v5gen-rewrite.h"
+# include "v5gen.h"
+# include "der.h"
+
+# include "akimpersonate_v5gen.h"
+
+int
+akv5gen_encode_krb5_ticket(int kvno,
+                           char *realm,
+                           int name_type,
+                           int name_len,
+                           char **name_parts,
+                           int enctype,
+                          size_t cipher_len,
+                          char *cipher_data,
+                          size_t *a_out_len,
+                          char **a_out_data)
+{
+    Ticket v5gen_tkt;
+    int code = 0;
+    size_t dummy;
+    char *outdata = NULL;
+    size_t outlen = 0;
+
+    memset(&v5gen_tkt, 0, sizeof(v5gen_tkt));
+
+    v5gen_tkt.tkt_vno = 5;
+    v5gen_tkt.realm = realm;
+
+    v5gen_tkt.sname.name_type = name_type;
+    v5gen_tkt.sname.name_string.len = name_len;
+    v5gen_tkt.sname.name_string.val = name_parts;
+
+    v5gen_tkt.enc_part.etype = enctype;
+    v5gen_tkt.enc_part.kvno = &kvno;
+    v5gen_tkt.enc_part.cipher.length = cipher_len;
+    v5gen_tkt.enc_part.cipher.data = cipher_data;
+
+    ASN1_MALLOC_ENCODE(Ticket, outdata, outlen,
+                       &v5gen_tkt, &dummy, code);
+    if (code == 0 && dummy != outlen)
+       code = EINVAL; /* XXX what error should this be? */
+    if (code)
+       goto cleanup;
+
+    *a_out_len = outlen;
+    *a_out_data = outdata;
+    outdata = NULL;
+
+ cleanup:
+    free(outdata);
+    return code;
+}
+
+int
+akv5gen_encode_krb5_enc_tkt_part(int enctype,
+                                 size_t key_len,
+                                 char *key_data,
+                                 char *realm,
+                                 int name_type,
+                                 int name_len,
+                                 char **name_parts,
+                                 int transited_type,
+                                 int transited_len,
+                                 char *transited_data,
+                                 time_t authtime,
+                                 time_t starttime,
+                                 time_t endtime,
+                                 time_t renew_till,
+                                size_t *a_out_len,
+                                char **a_out_data)
+{
+    EncTicketPart v5gen_enc;
+    size_t dummy;
+    int i;
+    int code = 0;
+    char *outdata = NULL;
+    size_t outlen = 0;
+
+    memset(&v5gen_enc, 0, sizeof(v5gen_enc));
+
+    /* assume the only flag that should be set is _INITIAL */
+    v5gen_enc.flags.initial = 1;
+
+    v5gen_enc.key.keytype = enctype;
+    v5gen_enc.key.keyvalue.length = key_len;
+    v5gen_enc.key.keyvalue.data = key_data;
+
+    v5gen_enc.crealm = realm;
+
+    v5gen_enc.cname.name_type = name_type;
+    v5gen_enc.cname.name_string.len = name_len;
+    v5gen_enc.cname.name_string.val = name_parts;
+
+    v5gen_enc.transited.tr_type = transited_type;
+    v5gen_enc.transited.contents.length = transited_len;
+    v5gen_enc.transited.contents.data = transited_data;
+
+    v5gen_enc.authtime = authtime;
+    v5gen_enc.starttime = &starttime;
+    v5gen_enc.endtime = endtime;
+    v5gen_enc.renew_till = &renew_till;
+
+    /* assume we have no addresses */
+    v5gen_enc.caddr = NULL;
+
+    /* assume we have no authz data */
+    v5gen_enc.authorization_data = NULL;
+
+    ASN1_MALLOC_ENCODE(EncTicketPart, outdata, outlen,
+                       &v5gen_enc, &dummy, code);
+    if (code == 0 && dummy != outlen)
+       code = EINVAL; /* XXX what error should this be? */
+    if (code)
+       goto cleanup;
+
+    *a_out_len = outlen;
+    *a_out_data = outdata;
+    outdata = NULL;
+
+ cleanup:
+    free(outdata);
+    return code;
+}
+
+#endif
diff --git a/src/auth/akimpersonate_v5gen.h b/src/auth/akimpersonate_v5gen.h
new file mode 100644 (file)
index 0000000..341452f
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __AKIMPERSONATE_V5GEN_H__
+#define __AKIMPERSONATE_V5GEN_H__
+extern int akv5gen_encode_krb5_ticket(int kvno,
+                                      char *realm,
+                                      int name_type,
+                                      int name_len,
+                                      char **name_parts,
+                                      int enctype,
+                                      size_t cipher_len,
+                                      char *cipher_data,
+                                      size_t *a_out_len,
+                                      char **a_out_data);
+
+extern int akv5gen_encode_krb5_enc_tkt_part(int enctype,
+                                            size_t key_len,
+                                            char *key_data,
+                                            char *realm,
+                                            int name_type,
+                                            int name_len,
+                                            char **name_parts,
+                                            int transited_type,
+                                            int transited_len,
+                                            char *transited_data,
+                                            time_t authtime,
+                                            time_t starttime,
+                                            time_t endtime,
+                                            time_t renew_till,
+                                            size_t *a_out_len,
+                                            char **a_out_data);
+#endif
index 85b59ec34d8652f16489691ca0df03009cf6e5d3..237fb45911664c18013148b5331e1a6c44164fa3 100644 (file)
 #include <des.h>
 #include <des_prototypes.h>
 #include <rx/rxkad.h>
-#ifdef USE_RXKAD_KEYTAB
+#if defined(USE_RXKAD_KEYTAB) && !defined(UKERNEL)
 #include <afs/dirpath.h>
+#include <krb5.h>
 #endif
 #include <rx/rx.h>
+#include <errno.h>
 #include "cellconfig.h"
 #include "keys.h"
 #include "auth.h"
+#if defined(USE_RXKAD_KEYTAB) && !defined(UKERNEL)
+#include "akimpersonate.h"
+#endif
 #endif /* defined(UKERNEL) */
 
 /* return a null security object if nothing else can be done */
@@ -107,6 +112,77 @@ afsconf_ServerAuth(register struct afsconf_dir *adir,
 }
 #endif /* !defined(UKERNEL) */
 
+#if defined(USE_RXKAD_KEYTAB) && !defined(UKERNEL)
+static afs_int32
+K5Auth(struct afsconf_dir *adir,
+       struct rx_securityClass **astr,
+       afs_int32 *aindex,
+       rxkad_level enclevel)
+{
+    struct rx_securityClass *tclass;
+    krb5_context context = NULL;
+    krb5_creds* fake_princ = NULL;
+    krb5_principal service_princ = NULL;
+    krb5_principal client_princ = NULL;
+    krb5_error_code r = 0;
+    struct ktc_encryptionKey session;
+    char *keytab_name = NULL;
+    size_t ktlen;
+
+    ktlen = 5 + strlen(adir->name) + 1 + strlen(AFSDIR_RXKAD_KEYTAB_FILE) + 1;
+    keytab_name = malloc(ktlen);
+    if (!keytab_name) {
+       return errno;
+    }
+    strcompose(keytab_name, ktlen, "FILE:", adir->name, "/",
+              AFSDIR_RXKAD_KEYTAB_FILE, (char *)NULL);
+
+    r = krb5_init_context(&context);
+    if (r)
+       goto cleanup;
+
+    r = krb5_build_principal(context, &client_princ, 1, "\0", "afs", NULL);
+    if (r)
+       goto cleanup;
+
+    r = get_credv5_akimpersonate(context, keytab_name,
+                                NULL, client_princ,
+                                0, 0x7fffffff,
+                                NULL,
+                                &fake_princ);
+
+    if (r == 0) {
+       if (tkt_DeriveDesKey(get_creds_enctype(fake_princ),
+                            get_cred_keydata(fake_princ),
+                            get_cred_keylen(fake_princ),
+                            &session) != 0) {
+           r = RXKADBADKEY;
+           goto cleanup;
+       }
+       tclass = (struct rx_securityClass *)
+            rxkad_NewClientSecurityObject(enclevel, &session,
+                                         RXKAD_TKT_TYPE_KERBEROS_V5,
+                                         fake_princ->ticket.length,
+                                         fake_princ->ticket.data);
+       if (tclass != NULL) {
+           *astr = tclass;
+           *aindex = 2;
+           r = 0;
+           goto cleanup;
+       }
+       r = 1;
+    }
+
+cleanup:
+    free(keytab_name);
+    if (fake_princ != NULL)
+       krb5_free_creds(context, fake_princ);
+    if (context != NULL)
+       krb5_free_context(context);
+    return r;
+}
+#endif
+
 static afs_int32
 GenericAuth(struct afsconf_dir *adir, 
            struct rx_securityClass **astr, 
@@ -120,6 +196,13 @@ GenericAuth(struct afsconf_dir *adir,
     afs_int32 ticketLen;
     register afs_int32 code;
 
+#if defined(USE_RXKAD_KEYTAB) && !defined(UKERNEL)
+    /* Try to do things the v5 way, before switching down to v4 */
+    code = K5Auth(adir, astr, aindex, enclevel);
+    if (code == 0)
+       return 0;
+#endif
+
     /* first, find the right key and kvno to use */
     code = afsconf_GetLatestKey(adir, &kvno, &key);
     if (code) {
index 12b89cc79c454a903b090e5fc068410db7f52b73..43fe9f4538bf4595d0f3ea34632c2e3269a66c7c 100644 (file)
@@ -32,7 +32,7 @@ AUTHOBJS = \
        writeconfig.o \
        authcon.o \
        ktc_errors.o \
-       acfg_errors.o @MAKE_KRB5@ akimpersonate.o
+       acfg_errors.o @MAKE_KRB5@ akimpersonate.o akimpersonate_v5gen.o
 
 KAUTHOBJS = \
        kauth.xdr.o \
@@ -125,6 +125,9 @@ authcon.o: ${AUTH}/authcon.c
 akimpersonate.o: ${AUTH}/akimpersonate.c
        ${CCRULE} -I../auth @KRB5CFLAGS@
 
+akimpersonate_v5gen.o: ${AUTH}/akimpersonate_v5gen.c
+       ${CCRULE} -I../auth @KRB5CFLAGS@ -I../rxkad
+
 ktc_errors.o: ${AUTH}/ktc_errors.c
        ${CCRULE}
 
index b93b38367e5f6c58efda3813f1ea2a6e0a4ab26f..e98b9b2503e12f54bac0ffedcd8b76256972998d 100644 (file)
@@ -37,7 +37,7 @@ AUTHOBJS = \
        writeconfig.o \
        authcon.o \
        ktc_errors.o \
-       acfg_errors.o @MAKE_KRB5@ akimpersonate.o
+       acfg_errors.o @MAKE_KRB5@ akimpersonate.o akimpersonate_v5gen.o
 
 KAUTHOBJS = \
        kauth.xdr.o \
@@ -155,6 +155,9 @@ authcon.o: ${AUTH}/authcon.c
 akimpersonate.o: ${AUTH}/akimpersonate.c
        ${CCRULE} @KRB5CFLAGS@
 
+akimpersonate_v5gen.o: ${AUTH}/akimpersonate_v5gen.c
+       ${CCRULE} @KRB5CFLAGS@ -I../rxkad
+
 ktc_errors.o: ${AUTH}/ktc_errors.c
        ${CCRULE}