aklog-use-native-principal-conversion-20090216
[openafs.git] / src / aklog / aklog_main.c
index 0200fc0..e29ce6f 100644 (file)
@@ -4,13 +4,40 @@
  * Copyright 1990,1991 by the Massachusetts Institute of Technology
  * For distribution and copying rights, see the file "mit-copyright.h"
  */
-
-#if !defined(lint) && !defined(SABER)
-static char *rcsid =
-       "$Id$";
-#endif /* lint || SABER */
+/*
+ * Copyright (c) 2005, 2006                                     
+ * The Linux Box Corporation                                    
+ * ALL RIGHTS RESERVED                                          
+ *                                                              
+ * Permission is granted to use, copy, create derivative works  
+ * and redistribute this software and such derivative works     
+ * for any purpose, so long as the name of the Linux Box        
+ * Corporation is not used in any advertising or publicity      
+ * pertaining to the use or distribution of this software       
+ * without specific, written prior authorization.  If the       
+ * above copyright notice or any other identification of the    
+ * Linux Box Corporation is included in any copy of any         
+ * portion of this software, then the disclaimer below must     
+ * also be included.                                            
+ *                                                              
+ * This software is provided as is, without representation      
+ * from the Linux Box Corporation as to its fitness for any     
+ * purpose, and without warranty by the Linux Box Corporation   
+ * of any kind, either express or implied, including            
+ * without limitation the implied warranties of                 
+ * merchantability and fitness for a particular purpose.  The   
+ * regents of the Linux Box Corporation shall not be liable     
+ * for any damages, including special, indirect, incidental, or 
+ * consequential damages, with respect to any claim arising     
+ * out of or in connection with the use of the software, even   
+ * if it has been or is hereafter advised of the possibility of 
+ * such damages.                                                
+ */
 
 #include <afsconfig.h>
+RCSID
+    ("$Header$");
+
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
@@ -29,7 +56,6 @@ static char *rcsid =
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#ifndef WINDOWS
 #include <sys/param.h>
 #include <sys/errno.h>
 #include <netdb.h>
@@ -37,39 +63,10 @@ static char *rcsid =
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <pwd.h>
-#endif /* WINDOWS */
-
-/* on AIX AFS has an unresolved reference to osi_audit. We will define
- * it here as extern. It also trys to call the ntohl and htonl routines
- * as routines rather then macros. We need a real routine here. 
- * We do this before the ntohl and htonl macros are defined in net/in.h
- */
-int osi_audit()
-    { return(0);}
-
-#if 0
-#ifdef _AIX
-u_long htonl(u_long x)
-    { return(x);}
-
-u_long ntohl(u_long x)
-    { return(x);}
-#endif
-
-#include <netinet/in.h>
-/* #include <krb.h> */
-#endif /* 0 */
 
 #include <afs/stds.h>
 #include <krb5.h>
 
-#ifdef WINDOWS
-
-#include <afs/auth.h>
-#include <rx/rxkad.h>
-#include <afs/dirpath.h>
-
-#else /* !WINDOWS */
 #ifndef HAVE_KERBEROSV_HEIM_ERR_H
 #include <afs/com_err.h>
 #endif
@@ -85,11 +82,17 @@ u_long ntohl(u_long x)
 #include <afs/ptserver.h>
 #include <afs/ptuser.h>
 #include <afs/dirpath.h>
-#endif /* WINDOWS */
 
 #include "aklog.h"
 #include "linked_list.h"
 
+#ifdef HAVE_KRB5_CREDS_KEYBLOCK
+#define USING_MIT 1
+#endif
+#ifdef HAVE_KRB5_CREDS_SESSION
+#define USING_HEIMDAL 1
+#endif
+
 #define AFSKEY "afs"
 #define AFSINST ""
 
@@ -97,6 +100,7 @@ u_long ntohl(u_long x)
 #define AFS_TRY_FULL_PRINC 1
 #endif /* AFS_TRY_FULL_PRINC */
 
+#define AKLOG_TRYAGAIN -1
 #define AKLOG_SUCCESS 0
 #define AKLOG_USAGE 1
 #define AKLOG_SOMETHINGSWRONG 2
@@ -138,16 +142,6 @@ static char linkedcell[MAXCELLCHARS+1];
 static char linkedcell2[MAXCELLCHARS+1];
 static krb5_ccache  _krb425_ccache = NULL;
 
-#ifdef WINDOWS
-
-/* libafsconf.dll */
-extern long cm_GetRootCellName();
-extern long cm_SearchCellFile();
-
-static long cm_SearchCellFile_CallBack();
-
-#else /* !WINDOWS */
-
 /*
  * Why doesn't AFS provide these prototypes?
  */
@@ -158,12 +152,16 @@ extern int pioctl(char *, afs_int32, struct ViceIoctl *, afs_int32);
  * Other prototypes
  */
 
-extern char *afs_realm_of_cell(krb5_context, struct afsconf_cell *);
+extern char *afs_realm_of_cell(krb5_context, struct afsconf_cell *, int);
 static int isdir(char *, unsigned char *);
 static krb5_error_code get_credv5(krb5_context context, char *, char *,
                                  char *, krb5_creds **);
 static int get_user_realm(krb5_context, char *);
 
+#define TRYAGAIN(x) (x == AKLOG_TRYAGAIN || \
+                    x == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || \
+                    x == KRB5KRB_ERR_GENERIC)
+
 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
 
 #define get_princ_str(c, p, n) krb5_princ_component(c, p, n)->data
@@ -184,6 +182,41 @@ static int get_user_realm(krb5_context, char *);
 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
 #endif
 
+#if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT) 
+krb5_error_code
+krb5_encrypt_tkt_part(krb5_context context,
+                     const krb5_keyblock *key,
+                     krb5_ticket *ticket)
+{
+    krb5_data *data = 0;
+    int code;
+    size_t enclen;
+    
+    if ((code = encode_krb5_enc_tkt_part(ticket->enc_part2, &data)))
+       goto Done;
+    if ((code = krb5_c_encrypt_length(context, key->enctype,
+                                     data->length, &enclen)))
+       goto Done;
+    ticket->enc_part.ciphertext.length = enclen;
+    if (!(ticket->enc_part.ciphertext.data = malloc(enclen))) {
+       code = ENOMEM;
+       goto Done;
+    }
+    if ((code = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_KDC_REP_TICKET,
+                              0, data, &ticket->enc_part))) {
+       free(ticket->enc_part.ciphertext.data);
+       ticket->enc_part.ciphertext.data = 0;
+    }
+Done:
+    if (data) {
+       if (data->data)
+           free(data->data);
+       free(data);
+    }
+    return code;
+}
+#endif
+
 #if defined(HAVE_KRB5_CREDS_KEYBLOCK)
 
 #define get_cred_keydata(c) c->keyblock.contents
@@ -203,10 +236,53 @@ static int get_user_realm(krb5_context, char *);
 #if !defined(HAVE_KRB5_524_CONVERT_CREDS) && defined(HAVE_KRB524_CONVERT_CREDS_KDC)
 #define krb5_524_convert_creds krb524_convert_creds_kdc
 #elif !defined(HAVE_KRB5_524_CONVERT_CREDS) && !defined(HAVE_KRB524_CONVERT_CREDS_KDC)
-#error "You must have one of krb5_524_convert_creds or krb524_convert_creds_kdc available"
+#define HAVE_NO_KRB5_524
+#endif
+
+#if USING_HEIMDAL
+#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_length(enc)                  \
+    ((enc)->cipher.length)
+
+#define deref_enc_data(enc)                    \
+    ((enc)->cipher.data)
+
+#define krb5_free_keytab_entry_contents krb5_kt_free_entry
+
+#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_length(enc)                  \
+    ((enc)->ciphertext.length)
+
+#define deref_enc_data(enc)                    \
+    ((enc)->ciphertext.data)
+
 #endif
 
-#endif /* WINDOWS */
+#define deref_entry_enctype(entry)                     \
+    deref_keyblock_enctype(&deref_entry_keyblock(entry))
 
 /*
  * Provide a replacement for strerror if we don't have it
@@ -223,10 +299,12 @@ static int noauth = FALSE;        /* If true, don't try to get tokens */
 static int zsubs = FALSE;      /* Are we keeping track of zephyr subs? */
 static int hosts = FALSE;      /* Are we keeping track of hosts? */
 static int noprdb = FALSE;     /* Skip resolving name to id? */
-static int linked = FALSE;  /* try for both AFS nodes */
-static int afssetpag = FALSE; /* setpag for AFS */
+static int linked = FALSE;      /* try for both AFS nodes */
+static int afssetpag = FALSE;   /* setpag for AFS */
 static int force = FALSE;      /* Bash identical tokens? */
 static int do524 = FALSE;      /* Should we do 524 instead of rxkad2b? */
+static char *keytab = NULL;     /* keytab for akimpersonate */
+static char *client = NULL;     /* client principal for akimpersonate */
 static linked_list zsublist;   /* List of zephyr subscriptions */
 static linked_list hostlist;   /* List of host addresses */
 static linked_list authedcells;        /* List of cells already logged to */
@@ -255,8 +333,6 @@ static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char *loc
     memset(local_cell, 0, sizeof(local_cell));
     memset((char *)cellconfig, 0, sizeof(*cellconfig));
 
-#ifndef WINDOWS
-
     if (!(configdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))) {
        fprintf(stderr, 
                "%s: can't get afs configuration (afsconf_Open(%s))\n",
@@ -283,101 +359,9 @@ static int get_cellconfig(char *cell, struct afsconf_cell *cellconfig, char *loc
 
     (void) afsconf_Close(configdir);
 
-#else /* WINDOWS */
-    /*
-     * We'll try to mimic the GetCellInfo call here and fill in as much
-     * of the afsconf_cell structure as we can.
-     */
-    if (cm_GetRootCellName(local_cell)) {
-       fprintf(stderr, "%s: can't get local cellname\n", progname);
-       exit(AKLOG_AFS);
-    }
-
-    if ((cell == NULL) || (cell[0] == 0))
-       cell = local_cell;
-
-    strcpy(cellconfig->name, cell);
-
-    /* No way of figuring this out as far as I can tell */
-    linkedcell[0] = '\0';
-
-    /* Initialize server info */
-    cellconfig->numServers = 0;
-    cellconfig->hostName[0][0] = "\0";
-
-    /*
-     * Get servers of cell. cm_SearchCellFile_CallBack() gets call with
-     * each server.
-     */
-    status = (int) cm_SearchCellFile(cell, NULL, &cm_SearchCellFile_CallBack,
-                                    cellconfig /* rock */);
-
-    switch(status) {
-    case 0:
-       break;
-
-    case -1:
-       fprintf(stderr, "%s: GetWindowsDirectory() failed.\n", progname);
-       break;
-
-    case -2:
-       fprintf(stderr, "%s: Couldn't open afsdcells.ini for reading\n",
-               progname);
-       break;
-
-    case -3:
-       fprintf(stderr, "%s: Couldn't find any servers for cell %s\n",
-               progname, cell);
-       break;
-
-    case -4:
-       fprintf(stderr, "%s: Badly formatted line in afsdcells.ini (does not begin with a \">\" or contain \"#\"\n",
-               progname);
-       break;
-
-    default:
-       fprintf(stderr, "%s cm_SearchCellFile returned unknown error %d\n",
-               status);
-    }
-
-    if (status) {
-       exit(AKLOG_AFS);
-    }
-
-    status = AKLOG_SUCCESS;
-
-    
-#endif /* WINDOWS */
-
     return(status);
 }
 
-
-#ifdef WINDOWS
-/*
- * Callback function for cm_SearchCellFile() in get_cellconfig() above.
- * This function gets called once for each server that is found for the cell.
- */
-static long
-cm_SearchCellFile_CallBack(void *rock /* cellconfig */,
-                          struct sockaddr_in *addr, /* Not used */
-                          char *server)
-{
-    struct afsconf_cell *cellconfig = rock;
-
-
-    /*
-     * Save server name and increment count of servers
-     */
-    strcpy(cellconfig->hostName[cellconfig->numServers++], server);
-    
-    return (long) 0;
-}
-
-    
-#endif /* WINDOWS */
-
-
 /* 
  * Log to a cell.  If the cell has already been logged to, return without
  * doing anything.  Otherwise, log to it and mark that it has been logged
@@ -389,39 +373,26 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
     char username[BUFSIZ];     /* To hold client username structure */
     afs_int32 viceId;          /* AFS uid of user */
 
-    char name[ANAME_SZ];       /* Name of afs key */
-    char primary_instance[INST_SZ];    /* Instance of afs key */
-    char secondary_instance[INST_SZ];  /* Backup instance to try */
-    int try_secondary = 0;             /* Flag to indicate if we try second */
     char realm_of_user[REALM_SZ]; /* Kerberos realm of user */
-    char realm_of_cell[REALM_SZ]; /* Kerberos realm of cell */
+    char *realm_from_princ = 0 ;  /* Calculated realm data */
+    char *realm_of_cell = 0;     /* Pointer to realm we're using */    
+    int retry;                   /* round, and round we go ... */
+    
     char local_cell[MAXCELLCHARS+1];
     char cell_to_use[MAXCELLCHARS+1]; /* Cell to authenticate to */
     static char lastcell[MAXCELLCHARS+1] = { 0 };
-#ifndef WINDOWS
     static char confname[512] = { 0 };
-#endif
     krb5_creds *v5cred = NULL;
     struct ktc_principal aserver;
     struct ktc_principal aclient;
     struct ktc_token atoken, btoken;
 
-#ifdef ALLOW_REGISTER
-    afs_int32 id;
-#endif /* ALLOW_REGISTER */
-
-    memset(name, 0, sizeof(name));
-    memset(primary_instance, 0, sizeof(primary_instance));
-    memset(secondary_instance, 0, sizeof(secondary_instance));
     memset(realm_of_user, 0, sizeof(realm_of_user));
-    memset(realm_of_cell, 0, sizeof(realm_of_cell));
 
-#ifndef WINDOWS
     if (confname[0] == '\0') {
        strncpy(confname, AFSDIR_CLIENT_ETC_DIRPATH, sizeof(confname));
        confname[sizeof(confname) - 2] = '\0';
     }
-#endif /* WINDOWS */
 
     /* NULL or empty cell returns information on local cell */
     if ((status = get_cellconfig(cell, &ak_cellconfig,
@@ -472,115 +443,177 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
                   cell_to_use, ak_cellconfig.hostName[0]);
        }
 
-       /*
-        * Find out which realm we're supposed to authenticate to.  If one
-        * is not included, use the kerberos realm found in the credentials
-        * cache.
-        */
-
-       if (realm && realm[0]) {
-           strcpy(realm_of_cell, realm);
-           if (dflag) {
-               printf("We were told to authenticate to realm %s.\n", realm);
-           }
-       }
-       else {
-           char *realm = afs_realm_of_cell(context, &ak_cellconfig);
-
-           if (!realm) {
-               fprintf(stderr, 
-                       "%s: Couldn't figure out realm for cell %s.\n",
-                       progname, cell_to_use);
-               exit(AKLOG_MISC);
-           }
-
-           strcpy(realm_of_cell, realm);
-
-           if (dflag) {
-               printf("We've deduced that we need to authenticate to"
-                      " realm %s.\n", realm_of_cell);
-           }
-       }
-
-       /* We use the afs.<cellname> convention here... 
-        *
-        * Doug Engert's original code had principals of the form:
-        *
-        * "afsx/cell@realm"
-        *
-        * in the KDC, so the name wouldn't conflict with DFS.  Since we're
-        * not using DFS, I changed it just to look for the following
-        * principals:
-        *
-        * afs/<cell>@<realm>
-        * afs@<realm>
-        *
-        * Because people are transitioning from afs@realm to afs/cell,
-        * we configure things so that if the first one isn't found, we
-        * try the second one.  You can select which one you prefer with
-        * a configure option.
-        */
-
-       strcpy(name, AFSKEY);
-
-       if (AFS_TRY_FULL_PRINC || strcasecmp(cell_to_use, realm_of_cell) != 0) {
-           strncpy(primary_instance, cell_to_use, sizeof(primary_instance));
-           primary_instance[sizeof(primary_instance)-1] = '\0';
-           if (strcasecmp(cell_to_use, realm_of_cell) == 0) {
-               try_secondary = 1;
-               secondary_instance[0] = '\0';
-           }
-       } else {
-           primary_instance[0] = '\0';
-           try_secondary = 1;
-           strncpy(secondary_instance, cell_to_use,
-                   sizeof(secondary_instance));
-           secondary_instance[sizeof(secondary_instance)-1] = '\0';
-       }
-
-       /* 
-        * Extract the session key from the ticket file and hand-frob an
-        * afs style authenticator.
-        */
-
-       /*
-        * Try to obtain AFS tickets.  Because there are two valid service
-        * names, we will try both, but trying the more specific first.
-        *
-        *      afs/<cell>@<realm> i.e. allow for single name with "."
-        *      afs@<realm>
-        */
-
-       if (dflag) {
-           printf("Getting tickets: %s/%s@%s\n", name,
-                  primary_instance, realm_of_cell);
+       if ((status = get_user_realm(context, realm_of_user))) {
+           fprintf(stderr, "%s: Couldn't determine realm of user:)",
+                   progname);
+           afs_com_err(progname, status, " while getting realm");
+           return(AKLOG_KERBEROS);
        }
 
-       status = get_credv5(context, name, primary_instance, realm_of_cell,
-                           &v5cred);
-
-       if (status == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || status == KRB5KRB_ERR_GENERIC) {
-           if (try_secondary) {
+       retry = 1;
+       
+       while(retry) {
+
+           /* This code tries principals in the following, much debated,
+            * order:
+            * 
+            * If the realm is specified on the command line we do
+            *    - afs/cell@COMMAND-LINE-REALM
+            *    - afs@COMMAND-LINE-REALM
+            * 
+            * Otherwise, we do
+            *    - afs/cell@REALM-FROM-USERS-PRINCIPAL
+            *    - afs/cell@krb5_get_host_realm(db-server)
+            *   Then, if krb5_get_host_realm(db-server) is non-empty
+            *      - afs@ krb5_get_host_realm(db-server)
+            *   Otherwise
+            *      - afs/cell@ upper-case-domain-of-db-server
+            *      - afs@ upper-case-domain-of-db-server
+            * 
+            * In all cases, the 'afs@' variant is only tried where the
+            * cell and the realm match case-insensitively.
+            */
+               
+           /* Cell on command line - use that one */
+           if (realm && realm[0]) {
+               realm_of_cell = realm;
+               status = AKLOG_TRYAGAIN;
+               if (dflag) {
+                   printf("We were told to authenticate to realm %s.\n", 
+                          realm);
+               }
+           } else {
+               /* Initially, try using afs/cell@USERREALM */
                if (dflag) {
-                   printf("Principal not found, trying alternate "
-                          "service name: %s/%s@%s\n", name,
-                           secondary_instance, realm_of_cell);
+                   printf("Trying to authenticate to user's realm %s.\n",
+                          realm_of_user);
                }
-               status = get_credv5(context, name, secondary_instance,
+               
+               realm_of_cell = realm_of_user;
+               status = get_credv5(context, AFSKEY, cell_to_use, 
                                    realm_of_cell, &v5cred);
+           
+               /* If that failed, try to determine the realm from the name of 
+                * one of the DB servers */
+               if (TRYAGAIN(status)) {
+                   realm_of_cell = afs_realm_of_cell(context, &ak_cellconfig, 
+                                                     FALSE);
+                   if (!realm_of_cell) {
+                       fprintf(stderr, 
+                               "%s: Couldn't figure out realm for cell %s.\n",
+                               progname, cell_to_use);
+                       exit(AKLOG_MISC);
+                   }
+
+                   if (dflag) {
+                       if (realm_of_cell[0])
+                           printf("We've deduced that we need to authenticate"
+                                  " to realm %s.\n", realm_of_cell);
+                   else
+                       printf("We've deduced that we need to authenticate "
+                              "using referrals.\n");
+                   }
+               }
+           }
+       
+           if (TRYAGAIN(status)) {
+               /* If we've got the full-princ-first option, or we're in a
+                * different realm from the cell - use the cell name as the
+                * instance */
+               if (AFS_TRY_FULL_PRINC || 
+                   strcasecmp(cell_to_use, realm_of_cell)!=0) {
+                   status = get_credv5(context, AFSKEY, cell_to_use, 
+                                       realm_of_cell, &v5cred);
+
+                   /* If we failed & we've got an empty realm, then try 
+                    * calling afs_realm_for_cell again. */
+                   if (TRYAGAIN(status) && !realm_of_cell[0]) {
+                       /* This time, get the realm by taking the domain 
+                        * component of the db server and make it upper case */
+                       realm_of_cell = afs_realm_of_cell(context, 
+                                                         &ak_cellconfig, TRUE);
+                       if (!realm_of_cell) {
+                           fprintf(stderr,
+                                   "%s: Couldn't figure out realm for cell "
+                                   "%s.\n", progname, cell_to_use);
+                           exit(AKLOG_MISC);
+                       }
+                       if (dflag) {
+                           printf("We've deduced that we need to authenticate"
+                                  " to realm %s.\n", realm_of_cell);
+                       }
+                   }
+                   status = get_credv5(context, AFSKEY, cell_to_use, 
+                                       realm_of_cell, &v5cred);
+               }
+          
+               /* If the realm and cell name match, then try without an 
+                * instance, but only if realm is non-empty */
+               
+               if (TRYAGAIN(status) && 
+                   strcasecmp(cell_to_use, realm_of_cell) == 0) {
+                   status = get_credv5(context, AFSKEY, NULL, 
+                                       realm_of_cell, &v5cred);
+                   if (!AFS_TRY_FULL_PRINC && TRYAGAIN(status)) {
+                       status = get_credv5(context, AFSKEY, cell_to_use,
+                                           realm_of_cell, &v5cred);
+                   }
+               }
            }
-       }
 
-       if (status) {
+           /* Try to find a service principal for this cell.
+            * Some broken MIT libraries return KRB5KRB_AP_ERR_MSG_TYPE upon 
+            * the first attempt, so we try twice to be sure */
+
+           if (status == KRB5KRB_AP_ERR_MSG_TYPE && retry == 1)
+               retry++;
+           else
+               retry = 0;
+       } 
+       
+       if (status != 0) {
            if (dflag) {
-               printf("Kerberos error code returned by get_cred: %d\n",
-                       status);
+               printf("Kerberos error code returned by get_cred : %d\n",
+                      status);
            }
            fprintf(stderr, "%s: Couldn't get %s AFS tickets:\n",
                    progname, cell_to_use);
-               com_err(progname, status, "while getting AFS tickets");
+           afs_com_err(progname, status, "while getting AFS tickets");
            return(AKLOG_KERBEROS);
        }
+       
+       /* If we've got a valid ticket, and we still don't know the realm name
+        * try to figure it out from the contents of the ticket
+        */
+#if !defined(USING_HEIMDAL) && defined(HAVE_KRB5_DECODE_TICKET)
+       if (strcmp(realm_of_cell, "") == 0) {
+           krb5_error_code code;
+           krb5_ticket *ticket;
+
+           code = krb5_decode_ticket(&v5cred->ticket, &ticket);
+
+           if (code != 0) {
+               fprintf(stderr,
+                       "%s: Couldn't decode ticket to determine realm for "
+                       "cell %s.\n",
+                       progname, cell_to_use);
+           } else {
+               int len = realm_len(context, ticket->server);
+               /* This really shouldn't happen. */
+               if (len > REALM_SZ-1)
+                   len = REALM_SZ-1;
+
+               realm_from_princ = (char *) malloc(sizeof(char) * (len+1));
+               
+               strncpy(realm_from_princ, realm_data(context, ticket->server), 
+                       len);
+               realm_from_princ[len] = 0;
+               realm_of_cell = realm_from_princ;
+               
+               krb5_free_ticket(context, ticket);
+           }
+       }
+#endif
 
        strncpy(aserver.name, AFSKEY, MAXKTCNAMELEN - 1);
        strncpy(aserver.instance, AFSINST, MAXKTCNAMELEN - 1);
@@ -599,19 +632,16 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
            if (dflag)
                printf("Using Kerberos V5 ticket natively\n");
 
-           len = min(get_princ_len(context, v5cred->client, 0),
-                     second_comp(context, v5cred->client) ?
-                                       MAXKTCNAMELEN - 2 : MAXKTCNAMELEN - 1);
-           strncpy(username, get_princ_str(context, v5cred->client, 0), len);
-           username[len] = '\0';
-
-           if (second_comp(context, v5cred->client)) {
-               strcat(username, ".");
-               p = username + strlen(username);
-               len = min(get_princ_len(context, v5cred->client, 1),
-                         MAXKTCNAMELEN - strlen(username) - 1);
-               strncpy(p, get_princ_str(context, v5cred->client, 1), len);
-               p[len] = '\0';
+           status = krb5_524_conv_principal (context, v5cred->client, &k4name, &k4inst, &k4realm);
+           if (status) {
+               afs_com_err(progname, status, "while converting principal "
+                       "to Kerberos V4 format");
+               return(AKLOG_KERBEROS);
+           }
+           strcpy (username, k4name);
+           if (k4inst[0]) {
+               strcat (username, ".");
+               strcat (username, k4inst);
            }
 
            memset(&atoken, 0, sizeof(atoken));
@@ -622,6 +652,7 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
                   get_cred_keylen(v5cred));
            atoken.ticketLen = v5cred->ticket.length;
            memcpy(atoken.ticket, v5cred->ticket.data, atoken.ticketLen);
+#ifndef HAVE_NO_KRB5_524
        } else {
            CREDENTIALS cred;
 
@@ -631,7 +662,7 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
            status = krb5_524_convert_creds(context, v5cred, &cred);
 
            if (status) {
-               com_err(progname, status, "while converting tickets "
+               afs_com_err(progname, status, "while converting tickets "
                        "to Kerberos V4 format");
                return(AKLOG_KERBEROS);
            }
@@ -656,6 +687,7 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
            memcpy(&atoken.sessionKey, cred.session, 8);
            atoken.ticketLen = cred.ticket_st.length;
            memcpy(atoken.ticket, cred.ticket_st.dat, atoken.ticketLen);
+#endif /* HAVE_NO_KRB5_524 */
        }
        
        if (!force &&
@@ -675,22 +707,13 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
        noprdb = 1;
 #endif
 
-#ifndef WINDOWS
        if (noprdb) {
-#endif
            if (dflag) {
                printf("Not resolving name %s to id (-noprdb set)\n",
                        username);
            }
-#ifndef WINDOWS
        }
        else {
-           if ((status = get_user_realm(context, realm_of_user))) {
-               fprintf(stderr, "%s: Couldn't determine realm of user:)",
-                       progname);
-               com_err(progname, status, " while getting realm");
-               return(AKLOG_KERBEROS);
-           }
            if (strcmp(realm_of_user, realm_of_cell)) {
                strcat(username, "@");
                strcat(username, realm_of_user);
@@ -729,19 +752,13 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
                 */
 
 #ifdef ALLOW_REGISTER
-       if (status == 0) {
-           if (viceId != ANONYMOUSID) {
-#else /* ALLOW_REGISTER */
-           if ((status == 0) && (viceId != ANONYMOUSID))
-#endif /* ALLOW_REGISTER */
-               sprintf (username, "AFS ID %d", (int) viceId);
-#ifdef ALLOW_REGISTER
-           } else if (strcmp(realm_of_user, realm_of_cell) != 0) {
+           if ((status == 0) && (viceId == ANONYMOUSID) &&
+               (strcmp(realm_of_user, realm_of_cell) != 0)) {
                if (dflag) {
                    printf("doing first-time registration of %s "
                            "at %s\n", username, cell_to_use);
                }
-               id = 0;
+               viceId = 0;
                strncpy(aclient.name, username, MAXKTCNAMELEN - 1);
                strcpy(aclient.instance, "");
                strncpy(aclient.cell, realm_of_user, MAXKTCREALMLEN - 1);
@@ -762,22 +779,23 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
                    printf("Error %d\n", status);
                }
 
-               if ((status = pr_CreateUser(username, &id))) {
+               if ((status = pr_CreateUser(username, &viceId))) {
                    fprintf(stderr, "%s: %s so unable to create remote PTS "
                            "user %s in cell %s (status: %d).\n", progname,
-                           error_message(status), username, cell_to_use,
+                           afs_error_message(status), username, cell_to_use,
                            status);
+                   viceId = ANONYMOUSID;
                } else {
-                   printf("created cross-cell entry for %s at %s\n",
-                          username, cell_to_use);
-                   sprintf(username, "AFS ID %d", (int) id);
+                   printf("created cross-cell entry for %s (Id %d) at %s\n",
+                          username, viceId, cell_to_use);
                }
            }
-       }
 #endif /* ALLOW_REGISTER */
 
+           if ((status == 0) && (viceId != ANONYMOUSID)) {
+               sprintf(username, "AFS ID %d", (int) viceId);
+           }
        }
-#endif /* !WINDOWS */
 
        if (dflag) {
            fprintf(stdout, "Set username to %s\n", username);
@@ -795,43 +813,20 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
            printf("Setting tokens. %s / %s @ %s \n",
                    aclient.name, aclient.instance, aclient.cell );
        }
+#ifndef AFS_AIX51_ENV
        /* on AIX 4.1.4 with AFS 3.4a+ if a write is not done before 
         * this routine, it will not add the token. It is not clear what 
-        * is going on here! So we will do the following operation
+        * is going on here! So we will do the following operation.
+        * On AIX 5, it causes the parent program to die, so we won't.
         */
        write(2,"",0); /* dummy write */
-#ifndef WINDOWS
+#endif
        if ((status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag))) {
            fprintf(stderr, 
                    "%s: unable to obtain tokens for cell %s (status: %d).\n",
                    progname, cell_to_use, status);
            status = AKLOG_TOKEN;
        }
-#else /* WINDOWS */
-       /* Note switched 2nd and 3rd args */
-       if ((status = ktc_SetToken(&aserver, &atoken, &aclient, afssetpag))) {
-           switch(status) {
-           case KTC_INVAL:
-               fprintf(stderr, "%s: Bad ticket length", progname);
-               break;
-           case KTC_PIOCTLFAIL:
-               fprintf(stderr, "%s: Unknown error contacting AFS service",
-                       progname);
-               break;
-           case KTC_NOCELL:
-               fprintf(stderr, "%s: Cell name (%s) not recognized by AFS service",
-                       progname, realm_of_cell);
-               break;
-           case KTC_NOCM:
-               fprintf(stderr, "%s: AFS service is unavailable", progname);
-               break;
-           default:
-               fprintf(stderr, "%s: Undocumented error (%d) contacting AFS service", progname, status);
-               break;  
-           }
-           status = AKLOG_TOKEN;           
-       }
-#endif /* !WINDOWS */
     }
     else
        if (dflag) {
@@ -841,8 +836,6 @@ static int auth_to_cell(krb5_context context, char *cell, char *realm)
     return(status);
 }
 
-#ifndef WINDOWS /* struct ViceIoctl missing */
-
 static int get_afs_mountpoint(char *file, char *mountpoint, int size)
 {
 #ifdef AFS_SUN_ENV
@@ -939,8 +932,8 @@ static char *next_path(char *origpath)
            ? elast_comp - last_comp : strlen(last_comp);
        strncat(pathtocheck, last_comp, len);
        memset(linkbuf, 0, sizeof(linkbuf));
-       if (link = (readlink(pathtocheck, linkbuf, 
-                                   sizeof(linkbuf)) > 0)) {
+       if ((link = (readlink(pathtocheck, linkbuf, 
+                                   sizeof(linkbuf)) > 0))) {
            if (++symlinkcount > MAXSYMLINKS) {
                fprintf(stderr, "%s: %s\n", progname, strerror(ELOOP));
                exit(AKLOG_BADPATH);
@@ -984,8 +977,6 @@ static char *next_path(char *origpath)
     return(pathtocheck);
 }
 
-#endif /* WINDOWS */
-
 #if 0
 /*****************************************/
 int dee_gettokens()
@@ -1016,8 +1007,6 @@ int dee_gettokens()
 /*****************************************/
 #endif
 
-#ifndef WINDOWS /* struct ViceIoctl missing */
-
 static void add_hosts(char *file)
 {
 #ifdef AFS_SUN_ENV
@@ -1077,10 +1066,6 @@ static void add_hosts(char *file)
     }
 }
 
-#endif /* WINDOWS */
-
-#ifndef WINDOWS /* next_path(), get_afs_mountpoint() */
-
 /*
  * This routine descends through a path to a directory, logging to 
  * every cell it encounters along the way.
@@ -1168,8 +1153,6 @@ static int auth_to_path(krb5_context context, char *path)
     return(status);
 }
 
-#endif /* WINDOWS */
-
 
 /* Print usage message and exit */
 static void usage(void)
@@ -1178,7 +1161,11 @@ static void usage(void)
            "[-d] [[-cell | -c] cell [-k krb_realm]] ",
            "[[-p | -path] pathname]\n",
            "    [-zsubs] [-hosts] [-noauth] [-noprdb] [-force] [-setpag] \n"
-           "    [-linked] [-524]\n");
+               "    [-linked]"
+#ifndef HAVE_NO_KRB5_524
+               " [-524]"
+#endif
+               "\n");
     fprintf(stderr, "    -d gives debugging information.\n");
     fprintf(stderr, "    krb_realm is the kerberos realm of a cell.\n");
     fprintf(stderr, "    pathname is the name of a directory to which ");
@@ -1190,7 +1177,9 @@ static void usage(void)
     fprintf(stderr, "    -force means replace identical tickets. \n");
     fprintf(stderr, "    -linked means if AFS node is linked, try both. \n");
     fprintf(stderr, "    -setpag set the AFS process authentication group.\n");
+#ifndef HAVE_NO_KRB5_524
     fprintf(stderr, "    -524 means use the 524 converter instead of V5 directly\n");
+#endif
     fprintf(stderr, "    No commandline arguments means ");
     fprintf(stderr, "authenticate to the local cell.\n");
     fprintf(stderr, "\n");
@@ -1240,9 +1229,7 @@ void aklog(int argc, char *argv[])
        progname = argv[0];
 
     krb5_init_context(&context);
-#ifndef WINDOWS
-       initialize_ktc_error_table ();
-#endif
+    initialize_ktc_error_table ();
 
     /* Initialize list of cells to which we have authenticated */
     (void)ll_init(&authedcells);
@@ -1263,8 +1250,10 @@ void aklog(int argc, char *argv[])
                linked++;
        else if (strcmp(argv[i], "-force") == 0)
            force++;
+#ifndef HAVE_NO_KRB5_524
        else if (strcmp(argv[i], "-524") == 0)
            do524++;
+#endif
     else if (strcmp(argv[i], "-setpag") == 0)
            afssetpag++;
        else if (((strcmp(argv[i], "-cell") == 0) ||
@@ -1275,34 +1264,34 @@ void aklog(int argc, char *argv[])
            }
            else
                usage();
+       else if ((strcmp(argv[i], "-keytab") == 0))
+           if (++i < argc) {
+               keytab = argv[i];
+           }
+           else
+               usage();
+       else if ((strcmp(argv[i], "-principal") == 0))
+           if (++i < argc) {
+               client = argv[i];
+           }
+           else
+               usage();
        else if (((strcmp(argv[i], "-path") == 0) ||
                  (strcmp(argv[i], "-p") == 0)) && !cmode)
-#ifndef WINDOWS
            if (++i < argc) {
                pmode++;
                strcpy(path, argv[i]);
            }
            else
                usage();
-#else /* WINDOWS */
-       {
-           fprintf(stderr, "%s: path mode not supported.\n", progname);
-           exit(AKLOG_MISC);
-       }
-#endif /* WINDOWS */
            
        else if (argv[i][0] == '-')
            usage();
        else if (!pmode && !cmode) {
            if (strchr(argv[i], DIR) || (strcmp(argv[i], ".") == 0) ||
                (strcmp(argv[i], "..") == 0)) {
-#ifndef WINDOWS
                pmode++;
                strcpy(path, argv[i]);
-#else /* WINDOWS */
-               fprintf(stderr, "%s: path mode not supported.\n", progname);
-               exit(AKLOG_MISC);
-#endif /* WINDOWS */
            }
            else { 
                cmode++;
@@ -1343,7 +1332,6 @@ void aklog(int argc, char *argv[])
            memset(cell, 0, sizeof(cell));
            memset(realm, 0, sizeof(realm));
        }
-#ifndef WINDOWS
        else if (pmode) {
            /* Add this path to list of paths */
            if ((cur_node = ll_add_node(&paths, ll_tail))) {
@@ -1364,7 +1352,6 @@ void aklog(int argc, char *argv[])
            pmode = FALSE;
            memset(path, 0, sizeof(path));
        }
-#endif /* WINDOWS */
     }
 
     /*
@@ -1416,7 +1403,6 @@ void aklog(int argc, char *argv[])
                                status = auth_to_cell(context, linkedcell2, NULL);
                }
 
-#ifndef WINDOWS
                /*
                 * Local hack - if the person has a file in their home
                 * directory called ".xlog", read that for a list of
@@ -1457,7 +1443,6 @@ void aklog(int argc, char *argv[])
                        }
                    }
                }
-#endif /* WINDOWS */
        }
     else {
        /* Log to all cells in the cells list first */
@@ -1479,13 +1464,11 @@ void aklog(int argc, char *argv[])
                }
        }
        
-#ifndef WINDOWS
        /* Then, log to all paths in the paths list */
        for (cur_node = paths.first; cur_node; cur_node = cur_node->next) {
            if ((status = auth_to_path(context, cur_node->data)))
                somethingswrong++;
        }
-#endif /* WINDOWS */
        
        /* 
         * If only one thing was logged to, we'll return the status 
@@ -1511,23 +1494,6 @@ void aklog(int argc, char *argv[])
     exit(status);
 }
 
-#ifndef HAVE_ADD_TO_ERROR_TABLE
-
-#define error_table error_table_compat
-#include <afs/error_table.h>
-#undef error_table
-
-#ifndef HAVE_ADD_ERROR_TABLE
-void add_error_table (const struct error_table *);
-#endif /* !HAVE_ADD_ERROR_TABLE */
-
-void
-add_to_error_table(struct et_list *new_table)
-{
-       add_error_table((struct error_table *) new_table->table);
-}
-#endif /* HAVE_ADD_TO_ERROR_TABLE */
-
 static int isdir(char *path, unsigned char *val)
 {
     struct stat statbuf;
@@ -1543,23 +1509,350 @@ static int isdir(char *path, unsigned char *val)
     }  
 }
 
+static 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];
+#else
+    krb5_ticket ticket_reply[1];
+    krb5_enc_tkt_part enc_tkt_reply[1];
+    krb5_address address[30], *faddr[30];
+#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) {
+        if (keytab)
+            afs_com_err(progname, code, "while resolving keytab %s", keytab);
+        else
+            afs_com_err(progname, code, "while resolving default keytab");
+        goto cleanup;
+    }
+
+    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) {
+           afs_com_err(progname, code,"while scanning keytab entries");
+           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))) {
+            afs_com_err(progname, code, "while starting keytab scan");
+            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))) {
+            afs_com_err(progname, i, "while ending keytab scan");
+            code = i;
+            goto cleanup;
+        }
+        if (best < 0) {
+            afs_com_err(progname, code, "while scanning keytab");
+            goto cleanup;
+        }
+        deref_keyblock_enctype(session_key) = deref_entry_enctype(entry);
+    }
+
+    /* Make Ticket */
+
+#if USING_HEIMDAL
+    if ((code = krb5_generate_random_keyblock(context,
+                                             deref_keyblock_enctype(session_key), session_key))) {
+        afs_com_err(progname, code, "while making 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->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))) {
+        afs_com_err(progname, code, "while making 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->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
+        }
+#if USING_HEIMDAL
+        faddr->len = i;
+#else
+        faddr[i] = 0;
+#endif
+    }
+
+#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) {
+            afs_com_err(progname, code, "while encoding ticket");
+            goto cleanup;
+        }
+
+        if(buf_len != buf_size) {
+            afs_com_err(progname, code,
+                   "%d != %d while encoding ticket (internal ASN.1 encoder error",
+                   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) {
+            afs_com_err(progname, code, "while %s", what);
+            goto cleanup;
+        }
+    } /* crypto block */
+    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))) {
+        afs_com_err(progname, code, "while making ticket");
+        goto cleanup;
+    }
+    ticket_reply->enc_part.kvno = entry->vno;
+#endif
+
+    /* Construct Creds */
+
+    if ((code = krb5_copy_principal(context, service_principal,
+                                   &creds->server))) {
+        afs_com_err(progname, code, "while copying service principal");
+        goto cleanup;
+    }
+    if ((code = krb5_copy_principal(context, client_principal,
+                                   &creds->client))) {
+        afs_com_err(progname, code, "while copying client principal");
+        goto cleanup;
+    }
+    if ((code = krb5_copy_keyblock_contents(context, session_key,
+                                           &deref_session_key(creds)))) {
+        afs_com_err(progname, code, "while copying session key");
+        goto cleanup;
+    }
+
+#if USING_HEIMDAL
+    creds->times.authtime = enc_tkt_reply->authtime;
+    creds->times.starttime = *(enc_tkt_reply->starttime);
+    creds->times.endtime = enc_tkt_reply->endtime;
+    creds->times.renew_till = 0; /* *(enc_tkt_reply->renew_till) */
+    creds->flags.b = enc_tkt_reply->flags;
+#else
+    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))) {
+        afs_com_err(progname, code, "while copying 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) {
+           afs_com_err(progname, code, "while encoding ticket");
+           goto cleanup;
+       }
+    }
+#else
+    if ((code = encode_krb5_ticket(ticket_reply, &temp))) {
+       afs_com_err(progname, code, "while encoding ticket");
+       goto cleanup;
+    }
+    creds->ticket = *temp;
+    free(temp);
+#endif
+    /* return creds */
+    *out_creds = creds;
+    creds = 0;
+cleanup:
+    if (deref_enc_data(&ticket_reply->enc_part))
+        free(deref_enc_data(&ticket_reply->enc_part));
+    krb5_free_keytab_entry_contents(context, entry);
+    if (client_principal)
+        krb5_free_principal(context, client_principal);
+    if (service_principal)
+        krb5_free_principal(context, service_principal);
+    if (cc)
+        krb5_cc_close(context, cc);
+    if (kt)
+        krb5_kt_close(context, kt);
+    if (creds) krb5_free_creds(context, creds);
+    krb5_free_keyblock_contents(context, session_key);
+out:
+    return code;
+#else
+    return -1;
+#endif
+}
+
+
 static krb5_error_code get_credv5(krb5_context context, 
-                       char *name, char *inst, char *realm,
-                       krb5_creds **creds)
+                                 char *name, char *inst, char *realm,
+                                 krb5_creds **creds)
 {
     krb5_creds increds;
     krb5_error_code r;
     static krb5_principal client_principal = 0;
 
+    if (dflag) {
+       printf("Getting tickets: %s%s%s@%s\n", name, (inst && inst[0])
+              ? "/" : "", inst ? inst : "", realm);
+    }
+    
     memset((char *)&increds, 0, sizeof(increds));
 /* ANL - instance may be ptr to a null string. Pass null then */
     if ((r = krb5_build_principal(context, &increds.server,
-                     strlen(realm), realm,
-                     name,
-           (inst && strlen(inst)) ? inst : (void *) NULL,
-                     (void *) NULL))) {
+                                 strlen(realm), realm,
+                                 name,
+                                 (inst && strlen(inst)) ? inst : (void *) NULL,
+                                 (void *) NULL))) {
         return r;
     }
+    
 
     if (!_krb425_ccache) {
         r = krb5_cc_default(context, &_krb425_ccache);
@@ -1567,18 +1860,36 @@ static krb5_error_code get_credv5(krb5_context context,
            return r;
     }
     if (!client_principal) {
-        r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
+       if (client) {
+           r = krb5_parse_name(context, client,  &client_principal);
+       } else {
+           r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
+       }
        if (r)
            return r;
     }
-
+    
     increds.client = client_principal;
     increds.times.endtime = 0;
-       /* Ask for DES since that is what V4 understands */
+    /* Ask for DES since that is what V4 understands */
     get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
-
-    r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
-
+    
+    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 */,
+                                    creds /* out */);
+    } else {
+       r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
+    }    
     return r;
 }
 
@@ -1587,16 +1898,24 @@ static int get_user_realm(krb5_context context, char *realm)
 {
     static krb5_principal client_principal = 0;
     int i;
+    krb5_error_code r = 0;
 
     if (!_krb425_ccache)
         krb5_cc_default(context, &_krb425_ccache);
-    if (!client_principal)
-        krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
+    if (!client_principal) {
+       if (client) {
+           r = krb5_parse_name(context, client,  &client_principal);
+       } else {
+           r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
+       }
+       if (r)
+           return r;
+    }
 
     i = realm_len(context, client_principal);
     if (i > REALM_SZ-1) i = REALM_SZ-1;
     strncpy(realm,realm_data(context, client_principal), i);
     realm[i] = 0;
 
-    return(0);
+    return(r);
 }