Clean up akimpersonate and use for server-to-server
authorBen Kaduk <kaduk@mit.edu>
Tue, 14 May 2013 23:37:59 +0000 (19:37 -0400)
committerSimon Wilkinson <sxw@your-file-system.com>
Wed, 17 Jul 2013 12:14:15 +0000 (13:14 +0100)
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).

Remove the early call to afsconf_GetLatestKey() in
afsconf_PickClientSecObj() so that we do not end up picking an old DES
key before we try to find a better key to use.

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 compatability routines.

UKERNEL doesn't need this stuff.

src/aklog/aklog.c
src/auth/Makefile.in
src/auth/akimpersonate.c
src/auth/akimpersonate.h
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/rxkad/Makefile.in
src/shlibafsauthent/Makefile.in
src/shlibafsrpc/libafsrpc.map

index 44857d0..89f73d8 100644 (file)
@@ -1809,17 +1809,12 @@ get_credv5(krb5_context context, char *name, char *inst, char *realm,
        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 e246c28..c1cf575 100644 (file)
@@ -14,15 +14,15 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 
 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
@@ -68,6 +68,9 @@ authcon.o: authcon.c ${INCLS}
 akimpersonate.o: akimpersonate.c ${INCLS}
        ${CCOBJ} ${CFLAGS} -c ${srcdir}/akimpersonate.c @KRB5_CPPFLAGS@
 
+akimpersonate_v5gen.o: akimpersonate_v5gen.c ${INCLS}
+       ${CCOBJ} ${CFLAGS} -c ${srcdir}/akimpersonate_v5gen.c @KRB5_CPPFLAGS@ -I${srcdir}/../rxkad
+
 userok.o: userok.c ${INCLS}
 cellconfig.o: cellconfig.c ${INCLS}
 copyauth.o: copyauth.c ${INCLS} AFS_component_version_number.o
index 2296c42..195807a 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;
+
+    *a_out = NULL;
+
+    out = calloc(1, sizeof(*out));
+    if (!out) {
+       code = ENOMEM;
+       goto cleanup;
+    }
 
-#if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT)
+    names = calloc(rep->server->length, sizeof(names[0]));
+    if (names == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
+
+    for (i = 0; i < rep->server->length; i++) {
+       names[i] = rep->server->data[i].data;
+    }
+
+    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;
+
+ cleanup:
+    free(names);
+    free(out);
+    return code;
+}
+# else
+extern krb5_error_code encode_krb5_ticket(krb5_ticket *rep,
+                                         krb5_data **a_out);
+# 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;
+
+    *a_out = NULL;
+
+    out = calloc(1, sizeof(*out));
+    if (out == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
+
+    names = calloc(encpart->client->length, sizeof(names[0]));
+    if (names == NULL) {
+       code = ENOMEM;
+       goto cleanup;
+    }
+
+    for (i = 0; i < encpart->client->length; i++)
+       names[i] = encpart->client->data[i].data;
+
+    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;
+
+    out->length = out_len;
+    out->data = out_data;
+    *a_out = out;
+    out = NULL;
+
+ cleanup:
+    free(names);
+    free(out);
+    return code;
+
+ invalid:
+    /* We don't handle all possible ticket options, features, etc. If we are
+     * given a ticket we can't handle, bail out with EINVAL. */
+    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,
@@ -89,286 +282,356 @@ Done:
     }
     return code;
 }
-#endif
+# else
+extern krb5_error_code krb5_encrypt_tkt_part(krb5_context context,
+                                            const krb5_keyblock *key,
+                                            krb5_ticket *ticket);
+# endif /* HAVE_KRB5_ENCRYPT_TKT_PART */
+#endif /* USING_MIT */
 
 static const int any_enctype[2] = {0, 0};
 static const krb5_data empty_string;
 
-#define deref_enc_tkt_addrs(tkt)               \
-    tkt->caddr
+/*
+ * 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.
+ */
+#if USING_HEIMDAL
+static int
+alloc_ticket(Ticket **out)
+{
+    *out = calloc(1, sizeof(Ticket));
+    if (*out == NULL)
+       return ENOMEM;
 
-#define deref_enc_length(enc)                  \
-    ((enc)->cipher.length)
+    (*out)->enc_part.kvno = malloc(sizeof(*(*out)->enc_part.kvno));
+    if ((*out)->enc_part.kvno == NULL)
+       return ENOMEM;
 
-#define deref_enc_data(enc)                    \
-    ((enc)->cipher.data)
+    return 0;
+}
+#else
+static int
+alloc_ticket(krb5_ticket **out)
+{
+    *out = calloc(1, sizeof(krb5_ticket));
+    if (*out == NULL)
+       return ENOMEM;
 
-#define krb5_free_keytab_entry_contents krb5_kt_free_entry
+    return 0;
+}
+#endif
 
+static void
+free_ticket(void *in)
+{
+#if USING_HEIMDAL
+    Ticket *ticket_reply;
 #else
-#define deref_keyblock_enctype(kb)             \
-    ((kb)->enctype)
-
-#define deref_entry_keyblock(entry)            \
-    entry->key
+    krb5_ticket *ticket_reply;
+#endif
 
-#define deref_session_key(creds)               \
-    creds->keyblock
+    /* requisite aliasing for MIT/Heimdal support. */
+    ticket_reply = in;
+    if (ticket_reply == NULL)
+       return;
 
-#define deref_enc_tkt_addrs(tkt)               \
-    tkt->caddrs
+#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);
+}
 
-#define deref_enc_length(enc)                  \
-    ((enc)->ciphertext.length)
+/*
+ * 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.
+ */
+#if USING_HEIMDAL
+static int
+alloc_enc_tkt_part(EncTicketPart **out)
+{
+    *out = calloc(1, sizeof(EncTicketPart));
+    if (*out == NULL)
+       return ENOMEM;
+
+    (*out)->starttime = malloc(sizeof(*(*out)->starttime));
+    if ((*out)->starttime == NULL)
+       return ENOMEM;
+    return 0;
+}
+#else
+static int
+alloc_enc_tkt_part(krb5_enc_tkt_part **out)
+{
+    *out = calloc(1, sizeof(krb5_enc_tkt_part));
+    if (*out == NULL)
+       return ENOMEM;
 
-#define deref_enc_data(enc)                    \
-    ((enc)->ciphertext.data)
+    return 0;
+}
+#endif
 
+static void
+free_enc_tkt_part(void *in)
+{
+#if USING_HEIMDAL
+    EncTicketPart *enc_tkt_reply;
+#else
+    krb5_enc_tkt_part *enc_tkt_reply;
 #endif
 
-#define deref_entry_enctype(entry)                     \
-    deref_keyblock_enctype(&deref_entry_keyblock(entry))
+    /* Aliasing for MIT/Heimdal support. */
+    enc_tkt_reply = in;
+    if (enc_tkt_reply == NULL)
+       return;
 
-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 */ )
-{
-#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];
+    if (enc_tkt_reply->starttime != NULL)
+       free(enc_tkt_reply->starttime);
 #else
-    krb5_ticket ticket_reply[1];
-    krb5_enc_tkt_part enc_tkt_reply[1];
-    krb5_address address[30], *faddr[30];
+    /* No allocations needed for MIT's krb5_enc_tkt_part structure. */
 #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;
+    free(enc_tkt_reply);
+}
 
-    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;
+/*
+ * 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);
+
+cleanup:
+    return code;
+}
 
-    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) {
+/*
+ * 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
+
+    /* 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;
+}
 
-    if ((code = krb5_copy_principal(context, service_principal,
-                                   &creds->server))) {
+/*
+ * 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;
+
+    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;
@@ -380,49 +643,133 @@ 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
+    *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
 }
index d1120d1..6b63fec 100644 (file)
@@ -16,6 +16,6 @@
 /* The caller must include krb5.h to get prototypes for the types used. */
 krb5_error_code
 get_credv5_akimpersonate(krb5_context, char*, krb5_principal, krb5_principal,
-                        time_t, time_t, int *, int *, krb5_creds**);
+                        time_t, time_t, const int *, krb5_creds**);
 
 #endif
diff --git a/src/auth/akimpersonate_v5gen.c b/src/auth/akimpersonate_v5gen.c
new file mode 100644 (file)
index 0000000..9d2e92e
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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;
+    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,
+                                 unsigned 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 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;
+    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..55d6343
--- /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,
+                                            unsigned 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 14c119f..22fc0cd 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 <afs/afsutil.h>
 #include "cellconfig.h"
 #include "keys.h"
 #include "auth.h"
+#if defined(USE_RXKAD_KEYTAB) && !defined(UKERNEL)
+#include "akimpersonate.h"
+#endif
 
 /* return a null security object if nothing else can be done */
 static afs_int32
@@ -88,6 +93,76 @@ afsconf_ServerAuth(void *arock,
 }
 #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 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 = RX_SECIDX_KAD;
+           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,
@@ -101,6 +176,13 @@ GenericAuth(struct afsconf_dir *adir,
     afs_int32 ticketLen;
     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) {
@@ -347,10 +429,6 @@ afsconf_PickClientSecObj(struct afsconf_dir *dir, afsconf_secflags flags,
            return AFSCONF_NOCELLDB;
 
        if (flags & AFSCONF_SECOPTS_LOCALAUTH) {
-           code = afsconf_GetLatestKey(dir, 0, 0);
-           if (code)
-               goto out;
-
            if (flags & AFSCONF_SECOPTS_ALWAYSENCRYPT)
                code = afsconf_ClientAuthSecure(dir, sc, scIndex);
            else
index 2fe13fb..6cc645a 100644 (file)
@@ -38,7 +38,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 \
@@ -153,6 +153,9 @@ authcon.o: ${AUTH}/authcon.c
 akimpersonate.o: ${AUTH}/akimpersonate.c
        ${CCRULE} -I../auth @KRB5_CPPFLAGS@
 
+akimpersonate_v5gen.o: ${AUTH}/akimpersonate_v5gen.c
+       ${CCRULE} -I../auth @KRB5_CPPFLAGS@ -I../rxkad
+
 ktc_errors.o: ${AUTH}/ktc_errors.c
        ${CCRULE}
 
index bddd6f7..2cce5db 100644 (file)
@@ -93,7 +93,7 @@ fcrypt.o: fcrypt.c fcrypt.h sboxes.h rxkad.h rxkad_prototypes.h
 crypt_conn.o: crypt_conn.c fcrypt.h private_data.h ${INCLS}
 
 ticket5_keytab.o: ticket5_keytab.c ${INCLS}
-       ${CCOBJ} ${CFLAGS} -c ${srcdir}/ticket5_keytab.c @KRB5_CPPFLAGS@
+       ${CC} ${CFLAGS} -c ${srcdir}/ticket5_keytab.c @KRB5_CPPFLAGS@
 
 tcrypt.o: tcrypt.c AFS_component_version_number.o
 
index 7b6f660..6d6a86b 100644 (file)
@@ -44,7 +44,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
 
 CMDOBJS = \
        cmd.o \
@@ -205,6 +205,9 @@ authcon.o: ${AUTH}/authcon.c
 akimpersonate.o: ${AUTH}/akimpersonate.c
        ${CCRULE} @KRB5_CPPFLAGS@
 
+akimpersonate_v5gen.o: ${AUTH}/akimpersonate_v5gen.c
+       ${CCRULE} @KRB5_CPPFLAGS@ -I../rxkad
+
 ktc_errors.o: ${AUTH}/ktc_errors.c
        ${CCRULE}
 
index b215b8f..e0a02cd 100755 (executable)
        rx_SetBusyChannelError;
        rx_KeepAliveOn;
        rx_KeepAliveOff;
+       _rxkad_v5_encode_EncTicketPart;
+       _rxkad_v5_encode_Ticket;
+       _rxkad_v5_length_EncTicketPart;
+       _rxkad_v5_length_Ticket;
     local:
        *;
 };