LINUX: Copy session keys to parent in SetToken
[openafs.git] / src / auth / ktc.c
index 6ee1b84..c04bc86 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
 /* ticket caching code */
 
 #include <afsconfig.h>
-#if defined(UKERNEL)
-#include "afs/param.h"
-#else
 #include <afs/param.h>
-#endif
 
+#include <roken.h>
 
-#if defined(UKERNEL)
-#include "afs/sysincludes.h"
-#include "afsincludes.h"
-#include "afs/stds.h"
-#include "afs/pthread_glock.h"
-#include "afs/vice.h"
-#include "afs/auth.h"
-#include "afs/venus.h"
-#include "afs/pthread_glock.h"
-#include "afs/dirpath.h"
+#include <afs/stds.h>
+#include <afs/opr.h>
+#include <afs/pthread_glock.h>
 #include <ctype.h>
 
-#if !defined(min)
-#define min(a,b) ((a)<(b)?(a):(b))
-#endif /* !defined(min) */
-
-#else /* defined(UKERNEL) */
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
 
-#ifdef AFS_SUN5_ENV
-#include <unistd.h>
+#if defined(UKERNEL)
+#include "afsincludes.h"
 #endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <afs/stds.h>
-#include <afs/pthread_glock.h>
-#include <sys/types.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <afs/vice.h>
+
 #ifdef AFS_AIX_ENV
 #include <sys/lockf.h>
 #ifdef AFS_AIX51_ENV
 #endif
 #endif
 #endif
-#ifdef AFS_DARWIN100_ENV
+
+#ifdef HAVE_CRT_EXTERNS_H
 #include <crt_externs.h>
 #endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <afs/auth.h>
+
+#include <afs/vice.h>
+#include "auth.h"
 #include <afs/venus.h>
 #include <afs/afsutil.h>
-#include <afs/sys_prototypes.h>
 
-#endif /* defined(UKERNEL) */
+#if !defined(UKERNEL)
+#include <afs/sys_prototypes.h>
+#endif
 
-#if defined(LINUX_KEYRING_SUPPORT) && defined(HAVE_SESSION_TO_PARENT)
+#if defined(AFS_LINUX26_ENV)
 #include <sys/syscall.h>
+#if defined(SYS_keyctl)
+/* Open code this value to avoid a dependency on keyutils */
 #define KEYCTL_SESSION_TO_PARENT        18
 #endif
+#endif
 
-/* For malloc() */
-#include <stdlib.h>
+#include "token.h"
 #include "ktc.h"
 
-#ifdef notdef
-/* AFS_KERBEROS_ENV is now conditionally defined in the Makefile */
-#define AFS_KERBEROS_ENV
-#endif
-
 #ifdef AFS_KERBEROS_ENV
-#include <fcntl.h>
-#include <sys/file.h>
-#include <afs/cellconfig.h>
+#include "cellconfig.h"
 static char lcell[MAXCELLCHARS];
 
 #define TKT_ROOT "/tmp/tkt"
@@ -116,9 +88,6 @@ static char lcell[MAXCELLCHARS];
 #define BUFSIZ 4096
 #endif
 
-#ifdef AFS_HPUX_ENV
-#include <unistd.h>
-#endif
 #if    defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV)
 static struct flock fileWlock = { F_WRLCK, 0, 0, 0, 0, 0 };
 static struct flock fileRlock = { F_RDLCK, 0, 0, 0, 0, 0 };
@@ -141,7 +110,7 @@ int afs_tf_init(char *, int);
 int afs_tf_get_pname(char *);
 int afs_tf_get_pinst(char *);
 int afs_tf_get_cred(struct ktc_principal *, struct ktc_token *);
-int afs_tf_save_cred(struct ktc_principal *, struct ktc_token *, 
+int afs_tf_save_cred(struct ktc_principal *, struct ktc_token *,
                     struct ktc_principal *);
 int afs_tf_close(void);
 int afs_tf_create(char *, char *);
@@ -149,94 +118,12 @@ int afs_tf_dest_tkt(void);
 static void ktc_LocalCell(void);
 #endif /* AFS_KERBEROS_ENV */
 
-#ifdef AFS_DUX40_ENV
-#define PIOCTL afs_pioctl
-#elif defined(UKERNEL)
-#define PIOCTL(A,B,C,D) call_syscall(AFSCALL_PIOCTL,A,B,C,D)
+#if defined(UKERNEL)
+#define PIOCTL(A,B,C,D) (errno = (call_syscall(AFSCALL_PIOCTL,A,B,C,D)), errno?-1:0)
 #else
 #define PIOCTL pioctl
 #endif
 
-
-#ifdef KERNEL_KTC_COMPAT
-
-#ifndef KTC_SYSCALL
-#define KTC_SYSCALL    32
-#endif
-
-/* Kernel call opcode definitions */
-#define KTC_OPCODE_BASE                4300
-#define KTC_NO_OP              (0+KTC_OPCODE_BASE)
-#define KTC_SETTOKEN_OP        (1+KTC_OPCODE_BASE)
-#define KTC_GETTOKEN_OP        (2+KTC_OPCODE_BASE)
-#define KTC_LISTTOKENS_OP      (3+KTC_OPCODE_BASE)
-#define KTC_FORGETTOKEN_OP     (4+KTC_OPCODE_BASE)
-#define KTC_FORGETALLTOKENS_OP  (5+KTC_OPCODE_BASE)
-#define KTC_STATUS_OP          (6+KTC_OPCODE_BASE)
-#define KTC_GC_OP              (7+KTC_OPCODE_BASE)
-
-#define KTC_INTERFACE_VERSION 3
-
-/* We have to determine if the kernel supports the ktc system call.  To do so
- * we attempt to execute its noop function.  If this is successful we use the
- * kernel calls in the future otherwise we let the old code run. */
-
-/* To safely check to see whether a system call exists we have to intercept the
- * SIGSYS signal which is caused by executing a non-existant system call.  If
- * it is ignored the syscall routine returns EINVAL.  The SIGSYS is reset to
- * its old value after the return from syscall.  */
-
-static int kernelKTC = 0;
-
-#ifdef AFS_DECOSF_ENV
-/*
- * SIGSYS semantics are broken on Dec AXP OSF/1 v1.2 systems.  You need
- * to ignore SIGTRAP too.  It is claimed to be fixed under v1.3, but...
- */
-
-#define CHECK_KERNEL                                                           \
-    if (kernelKTC == 0) {                                              \
-       int code, ecode;                                                \
-       int (*old)();                                                   \
-       int (*old_t)();                                                 \
-       old = (int (*)())signal(SIGSYS, SIG_IGN);                       \
-       old_t = (int (*)())signal(SIGTRAP, SIG_IGN);                    \
-       code = syscall (KTC_SYSCALL, KTC_NO_OP, 0,0,0,0,0);             \
-       ecode = errno;                                                  \
-       signal(SIGSYS, old);                                            \
-       signal(SIGTRAP, old_t);                                         \
-       if (code == 0) kernelKTC = 1;                                   \
-       else kernelKTC = 2;                                             \
-/* printf ("returned from KTC_NO_OP kernelKTC <= %d; code=%d, errno=%d\n", kernelKTC, code, errno); */\
-    }
-
-#else /* AFS_DECOSF_ENV */
-
-#define CHECK_KERNEL                                                           \
-    if (kernelKTC == 0) {                                              \
-       int code, ecode;                                                \
-       int (*old)();                                                   \
-       old = (int (*)())signal(SIGSYS, SIG_IGN);                       \
-       code = syscall (KTC_SYSCALL, KTC_NO_OP, 0,0,0,0,0);             \
-       ecode = errno;                                                  \
-       signal(SIGSYS, old);                                            \
-       if (code == 0) kernelKTC = 1;                                   \
-       else kernelKTC = 2;                                             \
-/* printf ("returned from KTC_NO_OP kernelKTC <= %d; code=%d, errno=%d\n", kernelKTC, code, errno); */\
-    }
-#endif /* AFS_DECOSF_ENV */
-
-#define TRY_KERNEL(cmd,a1,a2,a3,a4)                                    \
-{   CHECK_KERNEL;                                                      \
-    if (kernelKTC == 1)                                                        \
-       return syscall (KTC_SYSCALL, cmd,                               \
-                       KTC_INTERFACE_VERSION, a1,a2,a3,a4);            \
-}
-
-#else
-#define TRY_KERNEL(cmd,a1,a2,a3,a4)
-#endif /* KERNEL_KTC_COMPAT */
-
 #if !defined(UKERNEL)
 /* this is a structure used to communicate with the afs cache mgr, but is
  * otherwise irrelevant */
@@ -256,39 +143,25 @@ static struct {
     struct ktc_principal server;
     struct ktc_principal client;
     struct ktc_token token;
-} local_tokens[MAXLOCALTOKENS] = { {
-0}, {
-0}, {
-0}, {
-0}};
-
-/* new interface routines to the ticket cache.  Only handle afs service right
- * now. */
+} local_tokens[MAXLOCALTOKENS];
 
 static int
-NewSetToken(struct ktc_principal *aserver, 
-           struct ktc_token *atoken, 
-           struct ktc_principal *aclient, 
-            afs_int32 flags)
-{
-    TRY_KERNEL(KTC_SETTOKEN_OP, aserver, aclient, atoken,
-              sizeof(struct ktc_token));
-    /* no kernel ticket cache */
-    return EINVAL;
-}
+GetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
+          int atokenLen, struct ktc_principal *alicnet, afs_int32 *aviceid);
+
 
 #define MAXPIOCTLTOKENLEN \
 (3*sizeof(afs_int32)+MAXKTCTICKETLEN+sizeof(struct ClearToken)+MAXKTCREALMLEN)
 
 static int
-OldSetToken(struct ktc_principal *aserver, struct ktc_token *atoken, 
+SetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
            struct ktc_principal *aclient, afs_int32 flags)
 {
     struct ViceIoctl iob;
     char tbuffer[MAXPIOCTLTOKENLEN];
-    register char *tp;
+    char *tp;
     struct ClearToken ct;
-    register afs_int32 code;
+    afs_int32 code;
     afs_int32 temp;
 
     if (strcmp(aserver->name, "afs") != 0) {
@@ -305,8 +178,9 @@ OldSetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
                        0)) {
                    found = i;  /* replace existing entry */
                    break;
-               } else          /* valid, but no match */
-                   ;
+               } else {
+                   /* valid, but no match */
+               }
            } else
                found = i;      /* remember this empty slot */
        if (found == -1)
@@ -420,17 +294,94 @@ OldSetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
     }
 #else /* NO_AFS_CLIENT */
     code = PIOCTL(0, VIOCSETTOK, &iob, 0);
-#if defined(LINUX_KEYRING_SUPPORT) && defined(HAVE_SESSION_TO_PARENT)
-    /*
-     * If we're using keyring based PAGs and the SESSION_TO_PARENT keyctl
-     * is available, use it to copy the session keyring to the parent process
-     */
-    if (flags & AFS_SETTOK_SETPAG)
-       syscall(SYS_keyctl, KEYCTL_SESSION_TO_PARENT);
-#endif
 #endif /* NO_AFS_CLIENT */
     if (code)
        return KTC_PIOCTLFAIL;
+#if defined(AFS_LINUX26_ENV) && defined(SYS_keyctl)
+    else
+        /*
+         * If we're using keyring based PAGs and the SESSION_TO_PARENT keyctl
+         * is available, use it to copy the session keyring to the parent process
+         */
+        if (flags & AFS_SETTOK_SETPAG)
+            syscall(SYS_keyctl, KEYCTL_SESSION_TO_PARENT);
+#endif
+    return 0;
+}
+
+int
+ktc_SetTokenEx(struct ktc_setTokenData *token) {
+    struct ViceIoctl iob;
+    afs_int32 code;
+    XDR xdrs;
+
+    xdrlen_create(&xdrs);
+    if (!xdr_ktc_setTokenData(&xdrs, token))
+       return EINVAL;
+    iob.in_size = xdr_getpos(&xdrs);
+    xdr_destroy(&xdrs);
+
+    iob.in = malloc(iob.in_size);
+    if (iob.in == NULL)
+       return ENOMEM;
+
+    xdrmem_create(&xdrs, iob.in, iob.in_size, XDR_ENCODE);
+    if (!xdr_ktc_setTokenData(&xdrs, token))
+       return KTC_INVAL;
+    xdr_destroy(&xdrs);
+
+    iob.out = NULL;
+    iob.out_size = 0;
+
+    code = PIOCTL(0, VIOC_SETTOK2, &iob, 0);
+
+    free(iob.in);
+
+    /* If we can't use the new pioctl, then fallback to using the old
+     * one, with just the rxkad portion of the token we're being asked to
+     * set
+     */
+    if (code == -1 && errno == EINVAL) {
+       struct ktc_principal server, client;
+       struct ktc_token *rxkadToken;
+       afs_int32 flags;
+
+       /* With the growth of ticket sizes, a ktc_token is now 12k. Don't
+        * allocate it on the stack! */
+       rxkadToken = malloc(sizeof(*rxkadToken));
+       if (rxkadToken == NULL)
+           return ENOMEM;
+
+       code = token_extractRxkad(token, rxkadToken, &flags, &client);
+       if (code) {
+           free(rxkadToken);
+           return KTC_INVAL;
+       }
+
+       memset(&server, 0, sizeof(server));
+       strcpy(server.name, "afs");
+       if (strlcpy(server.cell, token->cell, sizeof(server.cell))
+               >= sizeof(server.cell)) {
+           free(rxkadToken);
+           return KTC_INVAL;
+       }
+       code = ktc_SetToken(&server, rxkadToken, &client, flags);
+       free(rxkadToken);
+       return code;
+    }
+
+    if (code)
+       return KTC_PIOCTLFAIL;
+#if defined(AFS_LINUX26_ENV) && defined(SYS_keyctl)
+    else
+       /*
+        * If we're using keyring based PAGs and the SESSION_TO_PARENT keyctl
+        * is available, use it to copy the session keyring to the parent process
+        */
+       if (token->flags & AFS_SETTOK_SETPAG)
+           syscall(SYS_keyctl, KEYCTL_SESSION_TO_PARENT);
+#endif
+
     return 0;
 }
 
@@ -440,7 +391,7 @@ ktc_SetToken(struct ktc_principal *aserver,
     struct ktc_principal *aclient,
     afs_int32 flags)
 {
-    int ncode, ocode;
+    int code;
 
     LOCK_GLOBAL_MUTEX;
 #ifdef AFS_KERBEROS_ENV
@@ -460,41 +411,36 @@ ktc_SetToken(struct ktc_principal *aserver,
            }
        }
 
-       ncode = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
-       if (ncode == NO_TKT_FIL) {
+       code = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
+       if (code == NO_TKT_FIL) {
            (void)afs_tf_create(aclient->name, aclient->instance);
-           ncode = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
+           code = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
        }
 
-       if (!ncode) {
+       if (!code) {
            afs_tf_save_cred(aserver, atoken, aclient);
        }
        afs_tf_close();
 #ifdef NO_AFS_CLIENT
        UNLOCK_GLOBAL_MUTEX;
-       return ncode;
+       return code;
 #endif /* NO_AFS_CLIENT */
     }
 #endif
 
 #ifndef NO_AFS_CLIENT
-    ncode = NewSetToken(aserver, atoken, aclient, flags);
-    if (ncode ||               /* new style failed */
-       (strcmp(aserver->name, "afs") == 0)) {  /* for afs tokens do both */
-       ocode = OldSetToken(aserver, atoken, aclient, flags);
-    } else
-       ocode = 0;
-    if (ncode && ocode) {
+    code = SetToken(aserver, atoken, aclient, flags);
+    if (code) {
        UNLOCK_GLOBAL_MUTEX;
-       if (ocode == -1)
-           ocode = errno;
-       else if (ocode == KTC_PIOCTLFAIL)
-           ocode = errno;
-       if (ocode == ESRCH)
+       if (code == -1)
+           code = errno;
+       else if (code == KTC_PIOCTLFAIL)
+           code = errno;
+       if (code == ESRCH)
            return KTC_NOCELL;
-       if (ocode == EINVAL)
+       if (code == EINVAL)
            return KTC_NOPIOCTL;
-       if (ocode == EIO)
+       if (code == EIO)
            return KTC_NOCM;
        return KTC_PIOCTLFAIL;
     }
@@ -503,31 +449,131 @@ ktc_SetToken(struct ktc_principal *aserver,
     return 0;
 }
 
+/*!
+ * Get a token, given the cell that we need to get information for
+ *
+ * @param cellName
+ *     The name of the cell we're getting the token for - if NULL, we'll
+ *     get information for the primary cell
+ */
+int
+ktc_GetTokenEx(char *cellName, struct ktc_setTokenData **tokenSet) {
+    struct ViceIoctl iob;
+    char tbuffer[MAXPIOCTLTOKENLEN];
+    char *tp;
+    afs_int32 code;
+    XDR xdrs;
+
+    tp = tbuffer;
+
+    /* If we have a cellName, write it out here */
+    if (cellName) {
+       memcpy(tp, cellName, strlen(cellName) +1);
+       tp += strlen(cellName)+1;
+    }
+
+    iob.in = tbuffer;
+    iob.in_size = tp - tbuffer;
+    iob.out = tbuffer;
+    iob.out_size = sizeof(tbuffer);
+
+    code = PIOCTL(0, VIOC_GETTOK2, &iob, 0);
+
+    /* If we can't use the new pioctl, the fall back to the old one. We then
+     * need to convert the rxkad token we get back into the new format
+     */
+    if (code == -1 && errno == EINVAL) {
+       struct ktc_principal server;
+       struct ktc_tokenUnion token;
+       struct ktc_token *ktcToken; /* too huge for the stack */
+       afs_int32 viceid;
+
+       memset(&server, 0, sizeof(server));
+       ktcToken = malloc(sizeof(struct ktc_token));
+       if (ktcToken == NULL)
+           return ENOMEM;
+       memset(ktcToken, 0, sizeof(struct ktc_token));
+
+       strcpy(server.name, "afs");
+
+       if (cellName != NULL)
+           strcpy(server.cell, cellName);
+
+       code = GetToken(&server, ktcToken, sizeof(struct ktc_token),
+                        NULL /*client*/, &viceid);
+       if (code == 0) {
+           *tokenSet = token_buildTokenJar(cellName);
+           token.at_type = AFSTOKEN_UNION_KAD;
+           token.ktc_tokenUnion_u.at_kad.rk_kvno = ktcToken->kvno;
+           memcpy(token.ktc_tokenUnion_u.at_kad.rk_key,
+                  ktcToken->sessionKey.data, 8);
+
+           token.ktc_tokenUnion_u.at_kad.rk_begintime = ktcToken->startTime;
+           token.ktc_tokenUnion_u.at_kad.rk_endtime   = ktcToken->endTime;
+           token.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len
+               = ktcToken->ticketLen;
+           token.ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val
+               = ktcToken->ticket;
+           token.ktc_tokenUnion_u.at_kad.rk_viceid = viceid;
+
+           token_addToken(*tokenSet, &token);
+
+           memset(ktcToken, 0, sizeof(struct ktc_token));
+       }
+       free(ktcToken);
+        return code;
+    }
+    if (code)
+       return KTC_PIOCTLFAIL;
+
+    *tokenSet = malloc(sizeof(struct ktc_setTokenData));
+    if (*tokenSet == NULL)
+       return ENOMEM;
+    memset(*tokenSet, 0, sizeof(struct ktc_setTokenData));
+
+    xdrmem_create(&xdrs, iob.out, iob.out_size, XDR_DECODE);
+    if (!xdr_ktc_setTokenData(&xdrs, *tokenSet)) {
+       free(*tokenSet);
+       *tokenSet = NULL;
+       xdr_destroy(&xdrs);
+       return EINVAL;
+    }
+    xdr_destroy(&xdrs);
+    return 0;
+}
+
 /* get token, given server we need and token buffer.  aclient will eventually
  * be set to our identity to the server.
  */
 int
-ktc_GetToken(struct ktc_principal *aserver, struct ktc_token *atoken, 
+ktc_GetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
             int atokenLen, struct ktc_principal *aclient)
 {
+    return GetToken(aserver, atoken, atokenLen, aclient, NULL);
+}
+
+static int
+GetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
+          int atokenLen, struct ktc_principal *aclient, afs_int32 *aviceid)
+{
     struct ViceIoctl iob;
     char tbuffer[MAXPIOCTLTOKENLEN];
-    register afs_int32 code = 0;
+    afs_int32 code = 0;
     int index;
     char *stp, *cellp;         /* secret token ptr */
     struct ClearToken ct;
-    register char *tp;
+    char *tp;
     afs_int32 temp;
     int maxLen;                        /* biggest ticket we can copy */
     int tktLen;                        /* server ticket length */
 #ifdef AFS_KERBEROS_ENV
     char found = 0;
 #endif
+    if (aviceid) {
+       *aviceid = 0;
+    }
 
     LOCK_GLOBAL_MUTEX;
-#ifndef NO_AFS_CLIENT
-    TRY_KERNEL(KTC_GETTOKEN_OP, aserver, aclient, atoken, atokenLen);
-#endif /* NO_AFS_CLIENT */
 
 #ifdef AFS_KERBEROS_ENV
     if (!lcell[0])
@@ -644,7 +690,7 @@ ktc_GetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
                /* got token for cell; check that it will fit */
                maxLen =
                    atokenLen - sizeof(struct ktc_token) + MAXKTCTICKETLEN;
-               if (maxLen < tktLen) {
+               if (tktLen < 0 || tktLen > maxLen) {
                    UNLOCK_GLOBAL_MUTEX;
                    return KTC_TOOBIG;
                }
@@ -661,15 +707,22 @@ ktc_GetToken(struct ktc_principal *aserver, struct ktc_token *atoken,
                       sizeof(struct ktc_encryptionKey));
                atoken->ticketLen = tktLen;
 
-               if (aclient) {
-                   strcpy(aclient->cell, cellp);
-                   aclient->instance[0] = 0;
+               if (aclient || aviceid) {
+                   if (aclient) {
+                       strlcpy(aclient->cell, cellp, sizeof(aclient->cell));
+                       aclient->instance[0] = 0;
+                   }
 
                    if ((atoken->kvno == 999) ||        /* old style bcrypt ticket */
                        (ct.BeginTimestamp &&   /* new w/ prserver lookup */
                         (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1))) {
-                       sprintf(aclient->name, "AFS ID %d", ct.ViceId);
-                   } else {
+                       if (aclient) {
+                           sprintf(aclient->name, "AFS ID %d", ct.ViceId);
+                       }
+                       if (aviceid) {
+                           *aviceid = ct.ViceId;
+                       }
+                   } else if (aclient) {
                        sprintf(aclient->name, "Unix UID %d", ct.ViceId);
                    }
                }
@@ -697,14 +750,93 @@ ktc_ForgetToken(struct ktc_principal *aserver)
     int rc;
 
     LOCK_GLOBAL_MUTEX;
-    TRY_KERNEL(KTC_FORGETTOKEN_OP, aserver, 0, 0, 0);
-
     rc = ktc_ForgetAllTokens();        /* bogus, but better */
     UNLOCK_GLOBAL_MUTEX;
     return rc;
 }
 #endif /* NO_AFS_CLIENT */
 
+/*!
+ * An iterator which can list all cells with tokens in the cache
+ *
+ * This function may be used to list the names of all cells for which
+ * tokens exist in the current cache. The first time that it is called,
+ * prevIndex should be set to 0. On all subsequent calls, prevIndex
+ * should be set to the value returned in newIndex by the last call
+ * to the function. Note that there is no guarantee that the index value
+ * is monotonically increasing.
+ *
+ * @param prevIndex
+ *     The index returned by the last call, or 0 if this is the first
+ *     call in an iteration
+ * @param newIndex
+ *     A pointer to an int which, upon return, will hold the next value
+ *     to be used.
+ * @param cellName
+ *     A pointer to a char * which, upon return, will hold a cellname.
+ *     This must be freed by the caller using free()
+ */
+
+int
+ktc_ListTokensEx(int prevIndex, int *newIndex, char **cellName) {
+    struct ViceIoctl iob;
+    char tbuffer[MAXPIOCTLTOKENLEN];
+    afs_int32 code;
+    afs_int32 index;
+    struct ktc_setTokenData tokenSet;
+    XDR xdrs;
+
+    memset(&tokenSet, 0, sizeof(tokenSet));
+
+    *cellName = NULL;
+    *newIndex = prevIndex;
+
+    index = prevIndex;
+
+    while (index<100) { /* Safety, incase of pioctl failure */
+       memset(tbuffer, 0, sizeof(tbuffer));
+       iob.in = tbuffer;
+       memcpy(tbuffer, &index, sizeof(afs_int32));
+       iob.in_size = sizeof(afs_int32);
+       iob.out = tbuffer;
+       iob.out_size = sizeof(tbuffer);
+
+       code = PIOCTL(0, VIOC_GETTOK2, &iob, 0);
+
+       if (code == -1 && errno == EDOM)
+           return KTC_NOENT;   /* no more tokens to be found */
+
+       /* Can't use new pioctl, so must use old one */
+       if (code == -1 && errno == EINVAL) {
+           struct ktc_principal server;
+
+           code = ktc_ListTokens(index, newIndex, &server);
+           if (code == 0)
+               *cellName = strdup(server.cell);
+           return code;
+       }
+
+       if (code == 0) {
+           /* Got a token from the pioctl. Now we throw it away,
+            * so we can return just a cellname. This is rather wasteful,
+            * but it's what the old API does. Ho hum.  */
+
+           xdrmem_create(&xdrs, iob.out, iob.out_size, XDR_DECODE);
+           if (!xdr_ktc_setTokenData(&xdrs, &tokenSet)) {
+               xdr_destroy(&xdrs);
+               return EINVAL;
+           }
+           xdr_destroy(&xdrs);
+           *cellName = strdup(tokenSet.cell);
+           xdr_free((xdrproc_t)xdr_ktc_setTokenData, &tokenSet);
+           *newIndex = index + 1;
+           return 0;
+       }
+       index++;
+    }
+    return KTC_PIOCTLFAIL;
+}
+
 /* ktc_ListTokens - list all tokens.  start aprevIndex at 0, it returns the
  * next rock in (*aindex).  (*aserver) is set to the relevant ticket on
  * success.  */
@@ -716,16 +848,13 @@ ktc_ListTokens(int aprevIndex,
 {
     struct ViceIoctl iob;
     char tbuffer[MAXPIOCTLTOKENLEN];
-    register afs_int32 code = 0 ;
-    register char *tp;
+    afs_int32 code = 0 ;
+    char *tp;
     afs_int32 temp, index;
 
     memset(tbuffer, 0, sizeof(tbuffer));
 
     LOCK_GLOBAL_MUTEX;
-#ifndef NO_AFS_CLIENT
-    TRY_KERNEL(KTC_LISTTOKENS_OP, aserver, aprevIndex, aindex, 0);
-#endif /* NO_AFS_CLIENT */
 
     index = aprevIndex;
 #ifdef NO_AFS_CLIENT
@@ -851,7 +980,7 @@ ktc_ListTokens(int aprevIndex,
     tp += temp;                        /* skip clear token itself */
     tp += sizeof(afs_int32);   /* skip primary flag */
     /* tp now points to the cell name */
-    strcpy(aserver->cell, tp);
+    strlcpy(aserver->cell, tp, sizeof(aserver->cell));
     aserver->instance[0] = 0;
     strcpy(aserver->name, "afs");
 #endif /* NO_AFS_CLIENT */
@@ -859,22 +988,11 @@ ktc_ListTokens(int aprevIndex,
     return 0;
 }
 
-/* discard all tokens from this user's cache */
-
 static int
-NewForgetAll(void)
-{
-#ifndef NO_AFS_CLIENT
-    TRY_KERNEL(KTC_FORGETALLTOKENS_OP, 0, 0, 0, 0);
-#endif /* NO_AFS_CLIENT */
-    return EINVAL;
-}
-
-static int
-OldForgetAll(void)
+ForgetAll(void)
 {
     struct ViceIoctl iob;
-    register afs_int32 code;
+    afs_int32 code;
     int i;
 
     for (i = 0; i < MAXLOCALTOKENS; i++)
@@ -895,16 +1013,15 @@ OldForgetAll(void)
 int
 ktc_ForgetAllTokens(void)
 {
-    int ncode, ocode;
+    int ocode;
 
     LOCK_GLOBAL_MUTEX;
 #ifdef AFS_KERBEROS_ENV
     (void)afs_tf_dest_tkt();
 #endif
 
-    ncode = NewForgetAll();
-    ocode = OldForgetAll();
-    if (ncode && ocode) {
+    ocode = ForgetAll();
+    if (ocode) {
        if (ocode == -1)
            ocode = errno;
        else if (ocode == KTC_PIOCTLFAIL)
@@ -924,16 +1041,7 @@ ktc_ForgetAllTokens(void)
 int
 ktc_OldPioctl(void)
 {
-    int rc;
-    LOCK_GLOBAL_MUTEX;
-#ifdef KERNEL_KTC_COMPAT
-    CHECK_KERNEL;
-    rc = (kernelKTC != 1);     /* old style interface */
-#else
-    rc = 1;
-#endif
-    UNLOCK_GLOBAL_MUTEX;
-    return rc;
+    return 1;
 }
 
 afs_uint32
@@ -963,14 +1071,14 @@ ktc_curpag(void)
        afs_uint32 g0, g1;
        afs_uint32 h, l, ret;
        int ngroups;
-#ifdef AFS_LINUX26_ENV
+#ifdef AFS_PAG_ONEGROUP_ENV
        int i;
 #endif
 
        ngroups = getgroups(sizeof groups / sizeof groups[0], groups);
 
-#ifdef AFS_LINUX26_ENV
-       /* check for AFS_LINUX26_ONEGROUP_ENV PAGs */
+#ifdef AFS_PAG_ONEGROUP_ENV
+       /* Check for one-group PAGs. */
        for (i = 0; i < ngroups; i++) {
            if (((groups[i] >> 24) & 0xff) == 'A') {
                return groups[i];
@@ -1011,15 +1119,6 @@ ktc_curpag(void)
   * <mit-copyright.h>.
   */
 
-#if 0
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <krb.h>
-#endif
-
 #define TOO_BIG -1
 #define TF_LCK_RETRY ((unsigned)2)     /* seconds to sleep before
                                         * retry if ticket file is
@@ -1036,7 +1135,7 @@ ktc_curpag(void)
  *        are invalid (ie. when deciding whether afs_tf_init has been
  *        called.)
  *     c. In tf_close, be sure it gets reinitialized to a negative
- *        number. 
+ *        number.
  */
 static int fd = -1;
 static int curpos;                     /* Position in tfbfr */
@@ -1086,7 +1185,7 @@ static int tf_read(char *, int);
  * afs_tf_close() closes the ticket file and releases the lock.
  *
  * tf_gets() returns the next null-terminated string.  It's an internal
- * routine used by afs_tf_get_pname(), afs_tf_get_pinst(), and 
+ * routine used by afs_tf_get_pname(), afs_tf_get_pinst(), and
  * afs_tf_get_cred().
  *
  * tf_read() reads a given number of bytes.  It's an internal routine
@@ -1096,13 +1195,13 @@ static int tf_read(char *, int);
 /*
  * afs_tf_init() should be called before the other ticket file routines.
  * It takes the name of the ticket file to use, "tf_name", and a
- * read/write flag "rw" as arguments. 
+ * read/write flag "rw" as arguments.
  *
  * It tries to open the ticket file, checks the mode, and if everything
  * is okay, locks the file.  If it's opened for reading, the lock is
- * shared.  If it's opened for writing, the lock is exclusive. 
+ * shared.  If it's opened for writing, the lock is exclusive.
  *
- * Returns 0 if all went well, otherwise one of the following: 
+ * Returns 0 if all went well, otherwise one of the following:
  *
  * NO_TKT_FIL   - file wasn't there
  * TKT_FIL_ACC  - file was in wrong mode, etc.
@@ -1142,7 +1241,7 @@ afs_tf_init(char *tf_name, int rw)
      * If "wflag" is set, open the ticket file in append-writeonly mode
      * and lock the ticket file in exclusive mode.  If unable to lock
      * the file, sleep and try again.  If we fail again, return with the
-     * proper error message. 
+     * proper error message.
      */
 
     curpos = sizeof(tfbfr);
@@ -1170,7 +1269,7 @@ afs_tf_init(char *tf_name, int rw)
     }
     /*
      * Otherwise "wflag" is not set and the ticket file should be opened
-     * for read-only operations and locked for shared access. 
+     * for read-only operations and locked for shared access.
      */
 
     fd = open(tf_name, O_RDONLY, 0600);
@@ -1200,7 +1299,7 @@ afs_tf_init(char *tf_name, int rw)
  * principal's name is filled into the "p" parameter.  If all goes well,
  * 0 is returned.  If afs_tf_init() wasn't called, TKT_FIL_INI is
  * returned.  If the name was null, or EOF was encountered, or the name
- * was longer than MAXKTCNAMELEN, TKT_FIL_FMT is returned. 
+ * was longer than MAXKTCNAMELEN, TKT_FIL_FMT is returned.
  */
 
 int
@@ -1221,7 +1320,7 @@ afs_tf_get_pname(char *p)
  * goes well, 0 is returned.  If afs_tf_init() wasn't called,
  * TKT_FIL_INI is returned.  If EOF was encountered, or the instance
  * was longer than MAXKTCNAMELEN, TKT_FIL_FMT is returned.  Note that the
- * instance may be null. 
+ * instance may be null.
  */
 
 int
@@ -1238,8 +1337,8 @@ afs_tf_get_pinst(char *inst)
 /*
  * afs_tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
  * in the given structure "c".  It should only be called after afs_tf_init(),
- * afs_tf_get_pname(), and afs_tf_get_pinst() have been called. If all goes 
- * well, 0 is returned.  Possible error codes are: 
+ * afs_tf_get_pname(), and afs_tf_get_pinst() have been called. If all goes
+ * well, 0 is returned.  Possible error codes are:
  *
  * TKT_FIL_INI  - afs_tf_init wasn't called first
  * TKT_FIL_FMT  - bad format
@@ -1340,9 +1439,9 @@ afs_tf_close(void)
  */
 
 static int
-tf_gets(register char *s, int n)
+tf_gets(char *s, int n)
 {
-    register int count;
+    int count;
 
     if (fd < 0) {
        return TKT_FIL_INI;
@@ -1375,9 +1474,9 @@ tf_gets(register char *s, int n)
  */
 
 static int
-tf_read(register char *s, register int n)
+tf_read(char *s, int n)
 {
-    register int count;
+    int count;
 
     for (count = n; count > 0; --count) {
        if (curpos >= sizeof(tfbfr)) {
@@ -1404,8 +1503,8 @@ tf_read(register char *s, register int n)
  */
 
 int
-afs_tf_save_cred(struct ktc_principal *aserver, 
-                struct ktc_token *atoken, 
+afs_tf_save_cred(struct ktc_principal *aserver,
+                struct ktc_token *atoken,
                 struct ktc_principal *aclient)
 {
     char realm[MAXKTCREALMLEN + 1];
@@ -1585,7 +1684,7 @@ afs_tf_create(char *pname, char *pinst)
     int count;
     char *file = ktc_tkt_string();
     int fd;
-    register int i;
+    int i;
     char zerobuf[1024];
     struct stat sbuf;
 
@@ -1698,11 +1797,12 @@ afs_tf_dest_tkt(void)
 int
 ktc_newpag(void)
 {
-#ifdef AFS_DARWIN100_ENV
-#define environ (*_NSGetEnviron())
-#else
+#if !defined(AFS_DARWIN100_ENV) || defined(HAVE_CRT_EXTERNS_H)
+# if defined(AFS_DARWIN100_ENV)
+#  define environ (*_NSGetEnviron())
+# else
 extern char **environ;
-#endif
+# endif
 
     afs_uint32 pag;
     struct stat sbuf;
@@ -1729,7 +1829,7 @@ extern char **environ;
 
     for (senv = environ, numenv = 0; *senv; senv++)
        numenv++;
-    newenv = (char **)malloc((numenv + 2) * sizeof(char *));
+    newenv = malloc((numenv + 2) * sizeof(char *));
 
     for (senv = environ, denv = newenv; *senv; senv++) {
        if (strncmp(*senv, "KRBTKFILE=", 10) != 0 &&
@@ -1747,6 +1847,7 @@ extern char **environ;
     *++denv = 0;
     environ = newenv;
     UNLOCK_GLOBAL_MUTEX;
+#endif
     return 0;
 }
 
@@ -1757,7 +1858,7 @@ extern char **environ;
 static void
 ktc_LocalCell(void)
 {
-    int code;
+    int code = 0;
     struct afsconf_dir *conf;
 
     if ((conf = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))