Add rxgk support to userok
[openafs.git] / src / auth / userok.c
index 8f28799..52afca3 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
@@ -9,59 +9,59 @@
 
 #include <afsconfig.h>
 #include <afs/param.h>
+#include <afs/stds.h>
 
-RCSID
-    ("$Header$");
+#include <roken.h>
+#include <afs/opr.h>
 
-#include <afs/stds.h>
-#include <afs/pthread_glock.h>
-#include <sys/types.h>
-#ifdef AFS_NT40_ENV
-#include <winsock2.h>
-#include <fcntl.h>
-#include <io.h>
-#else
-#include <sys/file.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#endif
-#include <sys/stat.h>
-#include <stdlib.h>            /* for realpath() */
-#include <errno.h>
-#include <string.h>
 #include <ctype.h>
 
+#include <afs/pthread_glock.h>
 #include <rx/xdr.h>
 #include <rx/rx.h>
-#include <stdio.h>
+#include <rx/rx_identity.h>
+#ifdef AFS_RXGK_ENV
+# include <rx/rxgk.h>
+#endif
 #include <afs/afsutil.h>
 #include <afs/fileutil.h>
 
-#ifdef AFS_ATHENA_STDENV
-#include <krb.h>
-#endif
-
+#include "base64.h"
 #include "auth.h"
 #include "cellconfig.h"
 #include "keys.h"
 #include "afs/audit.h"
 
-afs_int32 afsconf_SuperUser();
+/* The display names for localauth and noauth identities; they aren't used
+ * inside tickets or anything, but just serve as something to display in logs,
+ * etc. */
+#define AFS_LOCALAUTH_NAME "<LocalAuth>"
+#define AFS_LOCALAUTH_LEN  (sizeof(AFS_LOCALAUTH_NAME)-1)
+#define AFS_NOAUTH_NAME "<NoAuth>"
+#define AFS_NOAUTH_LEN  (sizeof(AFS_NOAUTH_NAME)-1)
+
+static int ParseLine(char *buffer, struct rx_identity *user);
+
+static void
+UserListFileName(struct afsconf_dir *adir,
+                char *buffer, size_t len)
+{
+    strcompose(buffer, len, adir->name, "/", AFSDIR_ULIST_FILE, (char *)NULL);
+}
 
-#if !defined(UKERNEL)
 int
-afsconf_CheckAuth(adir, acall)
-     register struct rx_call *acall;
-     register struct afsconf_dir *adir;
+afsconf_CheckAuth(void *arock, struct rx_call *acall)
 {
-    LOCK_GLOBAL_MUTEX return ((afsconf_SuperUser(adir, acall, NULL) == 0) ?
-                             10029 : 0);
-UNLOCK_GLOBAL_MUTEX}
-#endif /* !defined(UKERNEL) */
-
-static
-GetNoAuthFlag(adir)
-     struct afsconf_dir *adir;
+    struct afsconf_dir *adir = (struct afsconf_dir *) arock;
+    int rc;
+    LOCK_GLOBAL_MUTEX;
+    rc = ((afsconf_SuperUser(adir, acall, NULL) == 0) ? 10029 : 0);
+    UNLOCK_GLOBAL_MUTEX;
+    return rc;
+}
+
+static int
+GetNoAuthFlag(struct afsconf_dir *adir)
 {
     if (access(AFSDIR_SERVER_NOAUTH_FILEPATH, 0) == 0) {
        osi_audit(NoAuthEvent, 0, AUD_END);     /* some random server is running noauth */
@@ -71,22 +71,24 @@ GetNoAuthFlag(adir)
 }
 
 
-afsconf_GetNoAuthFlag(adir)
-     struct afsconf_dir *adir;
+int
+afsconf_GetNoAuthFlag(struct afsconf_dir *adir)
 {
     int rc;
 
-    LOCK_GLOBAL_MUTEX rc = GetNoAuthFlag(adir);
-    UNLOCK_GLOBAL_MUTEX return rc;
+    LOCK_GLOBAL_MUTEX;
+    rc = GetNoAuthFlag(adir);
+    UNLOCK_GLOBAL_MUTEX;
+    return rc;
 }
 
-afsconf_SetNoAuthFlag(adir, aflag)
-     struct afsconf_dir *adir;
-     int aflag;
+void
+afsconf_SetNoAuthFlag(struct afsconf_dir *adir, int aflag)
 {
-    register afs_int32 code;
+    afs_int32 code;
 
-    LOCK_GLOBAL_MUTEX if (aflag == 0) {
+    LOCK_GLOBAL_MUTEX;
+    if (aflag == 0) {
        /* turn off noauth flag */
        code = (unlink(AFSDIR_SERVER_NOAUTH_FILEPATH) ? errno : 0);
        osi_audit(NoAuthDisableEvent, code, AUD_END);
@@ -101,26 +103,56 @@ afsconf_SetNoAuthFlag(adir, aflag)
        } else
            osi_audit(NoAuthEnableEvent, errno, AUD_END);
     }
-UNLOCK_GLOBAL_MUTEX}
+    UNLOCK_GLOBAL_MUTEX;
+}
+
+/*!
+ * Remove an identity from the UserList file
+ *
+ * This function removes the given identity from the user list file.
+ * For the purposes of identifying entries to remove, only the
+ * type and exportedName portions of the identity are used. Callers
+ * should remember that a given identity may be listed in the file in
+ * a number of different ways.
+ *
+ * @param adir
+ *     A structure representing the configuration directory currently
+ *     in use
+ * @param user
+ *     The RX identity to delete
+ *
+ * @returns
+ *     0 on success, an error code on failure
+ */
 
-/* deletes a user from the UserList file */
-afsconf_DeleteUser(adir, auser)
-     struct afsconf_dir *adir;
-     register char *auser;
+int
+afsconf_DeleteIdentity(struct afsconf_dir *adir, struct rx_identity *user)
 {
-    char tbuffer[1024];
-    char nbuffer[1024];
-    register FILE *tf;
-    register FILE *nf;
-    register int flag;
-    char tname[64];
+    char *filename, *nfilename;
+    char *buffer;
+    char *copy;
+    FILE *tf;
+    FILE *nf;
+    int flag;
     char *tp;
     int found;
     struct stat tstat;
-    register afs_int32 code;
+    struct rx_identity identity;
+    afs_int32 code;
+
+    memset(&identity, 0, sizeof(struct rx_identity));
+
+    buffer = malloc(AFSDIR_PATH_MAX);
+    if (buffer == NULL)
+       return ENOMEM;
+    filename = malloc(AFSDIR_PATH_MAX);
+    if (filename == NULL) {
+       free(buffer);
+       return ENOMEM;
+    }
 
-    LOCK_GLOBAL_MUTEX strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
-                                AFSDIR_ULIST_FILE, NULL);
+    LOCK_GLOBAL_MUTEX;
+    UserListFileName(adir, filename, AFSDIR_PATH_MAX);
 #ifndef AFS_NT40_ENV
     {
        /*
@@ -128,342 +160,727 @@ afsconf_DeleteUser(adir, auser)
         * of the temporary file will work even if UserList is a symlink
         * into a different filesystem.
         */
-       char resolved_path[1024];
-
-       if (realpath(tbuffer, resolved_path)) {
-           strcpy(tbuffer, resolved_path);
+       nfilename = malloc(AFSDIR_PATH_MAX);
+       if (nfilename == NULL) {
+           UNLOCK_GLOBAL_MUTEX;
+           free(filename);
+           free(buffer);
+           return ENOMEM;
+       }
+       if (realpath(filename, nfilename)) {
+           free(filename);
+           filename = nfilename;
+       } else {
+           free(nfilename);
        }
     }
 #endif /* AFS_NT40_ENV */
-    tf = fopen(tbuffer, "r");
+    if (asprintf(&nfilename, "%s.NXX", filename) < 0) {
+       UNLOCK_GLOBAL_MUTEX;
+       free(filename);
+       free(buffer);
+       return -1;
+    }
+    tf = fopen(filename, "r");
     if (!tf) {
-       UNLOCK_GLOBAL_MUTEX return -1;
+       UNLOCK_GLOBAL_MUTEX;
+       free(filename);
+       free(nfilename);
+       free(buffer);
+       return -1;
     }
-    code = stat(tbuffer, &tstat);
+    code = stat(filename, &tstat);
     if (code < 0) {
-       UNLOCK_GLOBAL_MUTEX return code;
+       UNLOCK_GLOBAL_MUTEX;
+       free(filename);
+       free(nfilename);
+       free(buffer);
+       return code;
     }
-    strcpy(nbuffer, tbuffer);
-    strcat(nbuffer, ".NXX");
-    nf = fopen(nbuffer, "w+");
+    nf = fopen(nfilename, "w+");
     if (!nf) {
        fclose(tf);
-       UNLOCK_GLOBAL_MUTEX return EIO;
+       UNLOCK_GLOBAL_MUTEX;
+       free(filename);
+       free(nfilename);
+       free(buffer);
+       return EIO;
     }
     flag = 0;
     found = 0;
     while (1) {
        /* check for our user id */
-       tp = fgets(nbuffer, sizeof(nbuffer), tf);
+       tp = fgets(buffer, AFSDIR_PATH_MAX, tf);
        if (tp == NULL)
            break;
-       code = sscanf(nbuffer, "%64s", tname);
-       if (code == 1 && strcmp(tname, auser) == 0) {
+
+       copy = strdup(buffer);
+       if (copy == NULL) {
+           flag = 1;
+           break;
+       }
+       code = ParseLine(copy, &identity);
+       if (code == 0 && rx_identity_match(user, &identity)) {
            /* found the guy, don't copy to output file */
            found = 1;
        } else {
-           /* otherwise copy original line  to output */
-           fprintf(nf, "%s", nbuffer);
+           /* otherwise copy original line to output */
+           fprintf(nf, "%s", buffer);
        }
+       free(copy);
+       rx_identity_freeContents(&identity);
     }
     fclose(tf);
+    free(buffer);
     if (ferror(nf))
        flag = 1;
     if (fclose(nf) == EOF)
        flag = 1;
-    strcpy(nbuffer, tbuffer);
-    strcat(nbuffer, ".NXX");   /* generate new file name again */
     if (flag == 0) {
        /* try the rename */
-       flag = renamefile(nbuffer, tbuffer);
+       flag = rk_rename(nfilename, filename);
        if (flag == 0)
-           flag = chmod(tbuffer, tstat.st_mode);
+           flag = chmod(filename, tstat.st_mode);
     } else
-       unlink(nbuffer);
+       unlink(nfilename);
 
     /* finally, decide what to return to the caller */
-    UNLOCK_GLOBAL_MUTEX if (flag)
-         return EIO;           /* something mysterious went wrong */
+    UNLOCK_GLOBAL_MUTEX;
+    free(filename);
+    free(nfilename);
+    if (flag)
+       return EIO;             /* something mysterious went wrong */
     if (!found)
        return ENOENT;          /* entry wasn't found, no changes made */
     return 0;                  /* everything was fine */
 }
 
-/* returns nth super user from the UserList file */
-afsconf_GetNthUser(adir, an, abuffer, abufferLen)
-     struct afsconf_dir *adir;
-     afs_int32 an;
-     char *abuffer;
-     afs_int32 abufferLen;
+/*!
+ * Remove a legacy Kerberos 4 name from the UserList file.
+ *
+ * This function removes a Kerberos 4 name from the super user list. It
+ * can only remove names which were added by the afsconf_AddUser interface,
+ * or with an explicit Kerberos v4 type.
+ *
+ * @param[in] adir
+ *     A structure representing the configuration directory
+ * @param[in] name
+ *     The Kerberos v4 name to remove
+ *
+ * @returns
+ *     0 on success, an error code upon failure.
+ *
+ * Note that this function is deprecated. New callers should use
+ * afsconf_DeleteIdentity instead.
+ */
+
+int
+afsconf_DeleteUser(struct afsconf_dir *adir, char *name)
 {
-    char tbuffer[256];
-    register FILE *tf;
-    char tname[64];
-    register char *tp;
-    register int flag;
-    register afs_int32 code;
-
-    LOCK_GLOBAL_MUTEX strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
-                                AFSDIR_ULIST_FILE, NULL);
-    tf = fopen(tbuffer, "r");
-    if (!tf) {
-       UNLOCK_GLOBAL_MUTEX return 1;
+    struct rx_identity *user;
+    int code;
+
+    user = rx_identity_new(RX_ID_KRB4, name, name, strlen(name));
+    if (!user)
+       return ENOMEM;
+
+    code = afsconf_DeleteIdentity(adir, user);
+
+    rx_identity_free(&user);
+
+    return code;
+}
+
+/* This is a multi-purpose funciton for use by either
+ * GetNthIdentity or GetNthUser. The parameter 'id' indicates
+ * whether we are counting all identities (if true), or just
+ * ones which can be represented by the old-style interfaces
+ * We return -1 for EOF, 0 for success, and >0 for all errors.
+ */
+static int
+GetNthIdentityOrUser(struct afsconf_dir *dir, int count,
+                    struct rx_identity **identity, int id)
+{
+    bufio_p bp;
+    char *tbuffer;
+    struct rx_identity fileUser;
+    afs_int32 code;
+
+    tbuffer = malloc(AFSDIR_PATH_MAX);
+    if (tbuffer == NULL)
+       return ENOMEM;
+
+    LOCK_GLOBAL_MUTEX;
+    UserListFileName(dir, tbuffer, AFSDIR_PATH_MAX);
+    bp = BufioOpen(tbuffer, O_RDONLY, 0);
+    if (!bp) {
+       UNLOCK_GLOBAL_MUTEX;
+       free(tbuffer);
+       return -1;
     }
-    flag = 1;
     while (1) {
-       /* check for our user id */
-       tp = fgets(tbuffer, sizeof(tbuffer), tf);
-       if (tp == NULL)
+       code = BufioGets(bp, tbuffer, AFSDIR_PATH_MAX);
+       if (code < 0) {
+           code = -1;
+           break;
+       }
+
+       code = ParseLine(tbuffer, &fileUser);
+       if (code != 0)
            break;
-       code = sscanf(tbuffer, "%64s", tname);
-       if (code == 1 && an-- == 0) {
-           flag = 0;
+
+       if (id || fileUser.kind == RX_ID_KRB4)
+           count--;
+
+       if (count < 0)
            break;
+        else
+           rx_identity_freeContents(&fileUser);
+    }
+    if (code == 0) {
+       *identity = rx_identity_copy(&fileUser);
+       rx_identity_freeContents(&fileUser);
+    }
+
+    BufioClose(bp);
+
+    UNLOCK_GLOBAL_MUTEX;
+    free(tbuffer);
+    return code;
+}
+
+/*!
+ * Return the Nth super user identity from the UserList
+ *
+ * @param[in] dir
+ *     A structure representing the configuration directory
+ * @param[in] count
+ *     A count (from zero) of the entries to return from the
+ *     UserList
+ * @param[out] identity
+ *     A pointer to the Nth identity
+ * @returns
+ *      status code
+ * @retval 0 Success
+ * @retval -1 We have searched beyond the end of the list.
+ * @retval >0 Error
+ */
+
+int
+afsconf_GetNthIdentity(struct afsconf_dir *dir, int count,
+                      struct rx_identity **identity)
+{
+   return GetNthIdentityOrUser(dir, count, identity, 1);
+}
+
+/*!
+ * Return the Nth Kerberos v4 identity from the UserList
+ *
+ * This returns the Nth old, kerberos v4 style name from
+ * the UserList file. In counting entries it skips any other
+ * name types it encounters - so will hide any new-style
+ * identities from its callers.
+ *
+ * @param[in] dir
+ *     A structure representing the configuration directory
+ * @param[in] count
+ *     A count (from zero) of the entries to return from the
+ *     UserList
+ * @param abuffer
+ *     A string in which to write the name of the Nth identity
+ * @param abufferLen
+ *     The length of the buffer passed in abuffer
+ * @returns
+ *      status code
+ * @retval 0 Success
+ * @retval 1 Either an EPERM error, or we have searched beyond the end of the
+ *           list.
+ * @retval >1 All other errors.
+ *
+ * This function is deprecated, all new callers should use
+ * GetNthIdentity instead. This function is particularly dangerous
+ * as it will hide any new-style identities from callers. It is also
+ * impossible to distinguish an EPERM error from a normal end-of-file
+ * condition with this function.
+ */
+
+int
+afsconf_GetNthUser(struct afsconf_dir *adir, afs_int32 an, char *abuffer,
+                  afs_int32 abufferLen)
+{
+    struct rx_identity *identity;
+    int code;
+
+    code = GetNthIdentityOrUser(adir, an, &identity, 0);
+    if (code == 0) {
+       strlcpy(abuffer, identity->displayName, abufferLen);
+       rx_identity_free(&identity);
+    }
+    if (code == -1) {
+       /* The new functions use -1 to indicate EOF, but the old interface
+        * uses 1 to indicate EOF. */
+       code = 1;
+    }
+    return code;
+}
+
+/*!
+ * Parse a UserList list
+ *
+ * Parse a line of data from a UserList file
+ *
+ * This parses a line of data in a UserList, and populates the passed
+ * rx_identity structure with the information about the user.
+ *
+ * @param buffer       A string containing the line to be parsed
+ * @param user         The user structure to be populated
+ *
+ * Note that the user->displayName, and user->exportedName.val fields
+ * must be freed with free() by the caller.
+ *
+ * This function damages the buffer thats passed to it. Callers are
+ * expected to make a copy if they want the buffer preserved.
+ *
+ * @return
+ *     0 on success, non-zero on failure.
+ */
+
+static int
+ParseLine(char *buffer, struct rx_identity *user)
+{
+    char *ptr;
+    char *ename;
+    char *displayName;
+    char *decodedName;
+    char name[64+1];
+    int len;
+    int kind;
+    int code;
+
+    if (buffer[0] == ' ') { /* extended names have leading space */
+       ptr = buffer + 1;
+       code = sscanf(ptr, "%i", &kind);
+       if (code != 1)
+           return EINVAL;
+
+       strsep(&ptr, " "); /* skip the bit we just read with scanf */
+       ename = strsep(&ptr, " "); /* Pull out the ename */
+       displayName = strsep(&ptr, " "); /* Display name runs to the end */
+       if (ename == NULL || displayName == NULL)
+           return EINVAL;
+
+       decodedName = malloc(strlen(ename));
+       if (decodedName == NULL)
+           return ENOMEM;
+
+       len = base64_decode(ename, decodedName);
+       if (len<0) {
+           free(decodedName);
+           return EINVAL;
        }
+
+       rx_identity_populate(user, kind, displayName, decodedName, len);
+       free(decodedName);
+
+       return 0; /* Success ! */
     }
-    if (flag == 0)
-       strcpy(abuffer, tname);
-    fclose(tf);
-    UNLOCK_GLOBAL_MUTEX return flag;
+
+    /* No extended name, try for a legacy name */
+    code = sscanf(buffer, "%64s", name);
+    if (code != 1)
+       return EINVAL;
+
+    rx_identity_populate(user, RX_ID_KRB4, name, name, strlen(name));
+    return 0;
 }
 
-/* returns true iff user is in the UserList file */
-static
-FindUser(adir, auser)
-     struct afsconf_dir *adir;
-     register char *auser;
+/*!
+ * Check if a given identity is in the UserList file,
+ * and thus is a super user
+ *
+ * @param adir
+ *     A structure representing the configuration directory to check
+ * @param user
+ *     The identity to check
+ * @returns
+ *     True if the user is listed in the UserList, otherwise false
+ */
+
+int
+afsconf_IsSuperIdentity(struct afsconf_dir *adir,
+                       struct rx_identity *user)
 {
-    char tbuffer[256];
-    register bufio_p bp;
-    char tname[64];
-    register int flag;
-    register afs_int32 code;
-    int rc;
+    bufio_p bp;
+    char *tbuffer;
+    struct rx_identity fileUser;
+    int match;
+    afs_int32 code;
+
+    if (user->kind == RX_ID_SUPERUSER)
+       return 1;
+
+    tbuffer = malloc(AFSDIR_PATH_MAX);
+    if (tbuffer == NULL)
+       return 0;
 
-    strcompose(tbuffer, sizeof tbuffer, adir->name, "/", AFSDIR_ULIST_FILE,
-              NULL);
+    UserListFileName(adir, tbuffer, AFSDIR_PATH_MAX);
     bp = BufioOpen(tbuffer, O_RDONLY, 0);
-    if (!bp)
+    if (!bp) {
+       free(tbuffer);
        return 0;
-    flag = 0;
-    while (1) {
-       /* check for our user id */
-       rc = BufioGets(bp, tbuffer, sizeof(tbuffer));
-       if (rc < 0)
-           break;
-       code = sscanf(tbuffer, "%64s", tname);
-       if (code == 1 && strcmp(tname, auser) == 0) {
-           flag = 1;
+    }
+    match = 0;
+    while (!match) {
+       code = BufioGets(bp, tbuffer, AFSDIR_PATH_MAX);
+        if (code < 0)
            break;
-       }
+
+       code = ParseLine(tbuffer, &fileUser);
+       if (code != 0)
+          break;
+
+       match = rx_identity_match(user, &fileUser);
+
+       rx_identity_freeContents(&fileUser);
     }
     BufioClose(bp);
-    return flag;
+    free(tbuffer);
+    return match;
 }
 
 /* add a user to the user list, checking for duplicates */
-afsconf_AddUser(adir, aname)
-     struct afsconf_dir *adir;
-     char *aname;
+int
+afsconf_AddIdentity(struct afsconf_dir *adir, struct rx_identity *user)
 {
     FILE *tf;
-    register afs_int32 code;
-    char tbuffer[256];
-
-    LOCK_GLOBAL_MUTEX if (FindUser(adir, aname)) {
-       UNLOCK_GLOBAL_MUTEX return EEXIST;      /* already in the list */
+    afs_int32 code;
+    char *ename;
+    char *tbuffer;
+
+    LOCK_GLOBAL_MUTEX;
+    if (afsconf_IsSuperIdentity(adir, user)) {
+       UNLOCK_GLOBAL_MUTEX;
+       return EEXIST;          /* already in the list */
     }
 
-    strcompose(tbuffer, sizeof tbuffer, adir->name, "/", AFSDIR_ULIST_FILE,
-              NULL);
+    tbuffer = malloc(AFSDIR_PATH_MAX);
+    UserListFileName(adir, tbuffer, AFSDIR_PATH_MAX);
     tf = fopen(tbuffer, "a+");
+    free(tbuffer);
     if (!tf) {
-       UNLOCK_GLOBAL_MUTEX return EIO;
+       UNLOCK_GLOBAL_MUTEX;
+       return EIO;
+    }
+    if (user->kind == RX_ID_KRB4) {
+       fprintf(tf, "%s\n", user->displayName);
+    } else {
+       base64_encode(user->exportedName.val, user->exportedName.len,
+                     &ename);
+       fprintf(tf, " %d %s %s\n", user->kind, ename, user->displayName);
+       free(ename);
     }
-    fprintf(tf, "%s\n", aname);
     code = 0;
     if (ferror(tf))
        code = EIO;
     if (fclose(tf))
        code = EIO;
-    UNLOCK_GLOBAL_MUTEX return code;
+    UNLOCK_GLOBAL_MUTEX;
+    return code;
+}
+
+int
+afsconf_AddUser(struct afsconf_dir *adir, char *aname)
+{
+    struct rx_identity *user;
+    int code;
+
+    user = rx_identity_new(RX_ID_KRB4, aname, aname, strlen(aname));
+    if (user == NULL)
+       return ENOMEM;
+
+    code = afsconf_AddIdentity(adir, user);
+
+    rx_identity_free(&user);
+
+    return code;
 }
 
 /* special CompFindUser routine that builds up a princ and then
-       calls finduser on it. If found, returns char * to user string, 
+       calls finduser on it. If found, returns char * to user string,
        otherwise returns NULL. The resulting string should be immediately
        copied to other storage prior to release of mutex. */
-static char *
-CompFindUser(adir, name, sep, inst, realm)
-     struct afsconf_dir *adir;
-     char *name;
-     char *sep;
-     char *inst;
-     char *realm;
+static int
+CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst,
+            char *realm, struct rx_identity **identity)
 {
     static char fullname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
+    struct rx_identity *testId;
 
     /* always must have name */
     if (!name || !name[0]) {
-       return NULL;
+       return 0;
     }
-    strcpy(fullname, name);
+
+    if (strlcpy(fullname, name, sizeof(fullname)) >= sizeof(fullname))
+       return 0;
 
     /* might have instance */
     if (inst && inst[0]) {
        if (!sep || !sep[0]) {
-           return NULL;
+           return 0;
        }
 
-       strcat(fullname, sep);
-       strcat(fullname, inst);
+       if (strlcat(fullname, sep, sizeof(fullname)) >= sizeof(fullname))
+           return 0;
+
+       if (strlcat(fullname, inst, sizeof(fullname)) >= sizeof(fullname))
+           return 0;
     }
 
     /* might have realm */
     if (realm && realm[0]) {
-       strcat(fullname, "@");
-       strcat(fullname, realm);
+       if (strlcat(fullname, "@", sizeof(fullname)) >= sizeof(fullname))
+           return 0;
+
+       if (strlcat(fullname, realm, sizeof(fullname)) >= sizeof(fullname))
+           return 0;
+    }
+
+    testId = rx_identity_new(RX_ID_KRB4, fullname, fullname, strlen(fullname));
+    if (afsconf_IsSuperIdentity(adir, testId)) {
+       if (identity)
+            *identity = testId;
+       else
+            rx_identity_free(&testId);
+       return 1;
     }
 
-    if (FindUser(adir, fullname)) {
-       return fullname;
+    rx_identity_free(&testId);
+    return 0;
+}
+
+static int
+kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst,
+                 char *tcell, struct rx_identity **identity)
+{
+    char tcell_l[MAXKTCREALMLEN] = "";
+    int code;
+    afs_int32 islocal;
+    int flag;
+
+    /* generate lowercased version of cell name */
+    if (tcell)
+       opr_lcstring(tcell_l, tcell, sizeof(tcell_l));
+
+    code = afsconf_IsLocalRealmMatch(adir, &islocal, tname, tinst, tcell);
+    if (code) {
+       return 0;
+    }
+
+    /* start with no authorization */
+    flag = 0;
+
+    /* localauth special case */
+    if ((tinst == NULL || strlen(tinst) == 0) &&
+       (tcell == NULL || strlen(tcell) == 0)
+       && !strcmp(tname, AUTH_SUPERUSER)) {
+       if (identity)
+           *identity = rx_identity_new(RX_ID_KRB4, AFS_LOCALAUTH_NAME,
+                                       AFS_LOCALAUTH_NAME, AFS_LOCALAUTH_LEN);
+       flag = 1;
+
+       /* cell of connection matches local cell or one of the realms */
+    } else if (islocal) {
+       if (CompFindUser(adir, tname, ".", tinst, NULL, identity)) {
+           flag = 1;
+       }
+       /* cell of conn doesn't match local cell or realm */
     } else {
-       return NULL;
+       if (CompFindUser(adir, tname, ".", tinst, tcell, identity)) {
+           flag = 1;
+       } else if (CompFindUser(adir, tname, ".", tinst, tcell_l, identity)) {
+           flag = 1;
+       }
     }
+
+    return flag;
 }
 
+static int
+rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall,
+              struct rx_identity **identity)
+{
+    char tname[MAXKTCNAMELEN]; /* authentication from ticket */
+    char tinst[MAXKTCNAMELEN];
+    char tcell[MAXKTCREALMLEN];
+
+    afs_uint32 exp;
+    int code;
 
-/* make sure user authenticated on rx call acall is in list of valid
-    users. Copy the "real name" of the authenticated user into namep
-    if a pointer is passed.
-*/
+    /* get auth details from server connection */
+    code = rxkad_GetServerInfo(rx_ConnectionOf(acall), NULL, &exp, tname,
+                              tinst, tcell, NULL);
+    if (code)
+       return 0;               /* bogus connection/other error */
+
+    return kerberosSuperUser(adir, tname, tinst, tcell, identity);
+}
+
+#ifdef AFS_RXGK_ENV
+static int
+rxgkSuperUser(struct afsconf_dir *adir, struct rx_call *acall,
+             struct rx_identity **identity_out)
+{
+    struct rx_identity *identity = NULL;
+    int is_super = 0;
+
+    if (rxgk_GetServerInfo(rx_ConnectionOf(acall), NULL /*level*/, NULL /*expiry*/,
+                           &identity) != 0)
+        return 0;
+
+    if (afsconf_IsSuperIdentity(adir, identity)) {
+        is_super = 1;
+        if (identity_out != NULL) {
+            *identity_out = identity;
+            identity = NULL;
+        }
+    }
+    if (identity != NULL) {
+        rx_identity_free(&identity);
+    }
+    return is_super;
+}
+#endif /* AFS_RXGK_ENV */
+
+/*!
+ * Check whether the user authenticated on a given RX call is a super
+ * user or not. If they are, return a pointer to the identity of that
+ * user.
+ *
+ * @param[in] adir
+ *     The configuration directory currently in use
+ * @param[in] acall
+ *     The RX call whose authenticated identity is being checked
+ * @param[out] identity
+ *     The RX identity of the user. Caller must free this structure.
+ * @returns
+ *     True if the user is a super user, or if the server is running
+ *     in noauth mode. Otherwise, false.
+ */
 afs_int32
-afsconf_SuperUser(adir, acall, namep)
-     struct afsconf_dir *adir;
-     struct rx_call *acall;
-     char *namep;
+afsconf_SuperIdentity(struct afsconf_dir *adir, struct rx_call *acall,
+                     struct rx_identity **identity)
 {
-    register struct rx_connection *tconn;
-    register afs_int32 code;
+    struct rx_connection *tconn;
+    afs_int32 code;
     int flag;
 
-    LOCK_GLOBAL_MUTEX if (!adir) {
-       UNLOCK_GLOBAL_MUTEX return 0;
+    LOCK_GLOBAL_MUTEX;
+    if (!adir) {
+       UNLOCK_GLOBAL_MUTEX;
+       return 0;
     }
 
     if (afsconf_GetNoAuthFlag(adir)) {
-       if (namep)
-           strcpy(namep, "<NoAuth>");
-       UNLOCK_GLOBAL_MUTEX return 1;
+       if (identity)
+           *identity = rx_identity_new(RX_ID_KRB4, AFS_NOAUTH_NAME,
+                                       AFS_NOAUTH_NAME, AFS_NOAUTH_LEN);
+       UNLOCK_GLOBAL_MUTEX;
+       return 1;
     }
 
     tconn = rx_ConnectionOf(acall);
     code = rx_SecurityClassOf(tconn);
-    if (code == 0) {
-       UNLOCK_GLOBAL_MUTEX return 0;   /* not authenticated at all, answer is no */
-    } else if (code == 1) {
+    if (code == RX_SECIDX_NULL) {
+       UNLOCK_GLOBAL_MUTEX;
+       return 0;               /* not authenticated at all, answer is no */
+    } else if (code == RX_SECIDX_VAB) {
        /* bcrypt tokens */
-       UNLOCK_GLOBAL_MUTEX return 0;   /* not supported any longer */
-    } else if (code == 2) {
-       char tname[MAXKTCNAMELEN];      /* authentication from ticket */
-       char tinst[MAXKTCNAMELEN];
-       char tcell[MAXKTCREALMLEN];
-       char tcell_l[MAXKTCREALMLEN];
-       char *tmp;
-
-       /* keep track of which one actually authorized request */
-       char uname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
-
-       afs_uint32 exp;
-       static char lcell[MAXCELLCHARS] = "";
-       static char lrealm[AFS_REALM_SZ] = "";
-
-       /* get auth details from server connection */
-       code =
-           rxkad_GetServerInfo(acall->conn, NULL, &exp, tname, tinst, tcell,
-                               NULL);
-       if (code) {
-           UNLOCK_GLOBAL_MUTEX return 0;       /* bogus connection/other error */
-       }
-
-       /* don't bother checking anything else if tix have expired */
-#ifdef AFS_PTHREAD_ENV
-       if (exp < clock_Sec()) {
-#else
-       if (exp < FT_ApproxTime()) {
+       UNLOCK_GLOBAL_MUTEX;
+       return 0;               /* not supported any longer */
+    } else if (code == RX_SECIDX_KAD) {
+       flag = rxkadSuperUser(adir, acall, identity);
+       UNLOCK_GLOBAL_MUTEX;
+       return flag;
+#ifdef AFS_RXGK_ENV
+    } else if (code == RX_SECIDX_GK) {
+       flag = rxgkSuperUser(adir, acall, identity);
+       UNLOCK_GLOBAL_MUTEX;
+       return flag;
 #endif
-           UNLOCK_GLOBAL_MUTEX return 0;       /* expired tix */
-       }
+    } else {                   /* some other auth type */
+       UNLOCK_GLOBAL_MUTEX;
+       return 0;               /* mysterious, just say no */
+    }
+}
 
-       /* generate lowercased version of cell name */
-       strcpy(tcell_l, tcell);
-       tmp = tcell_l;
-       while (*tmp) {
-           *tmp = tolower(*tmp);
-           *tmp++;
-       }
+/*!
+ * Check whether the user authenticated on a given RX call is a super
+ * user or not. If they are, return a pointer to the name of that
+ * user.
+ *
+ * @param[in] adir
+ *     The configuration directory currently in use
+ * @param[in] acall
+ *     The RX call whose authenticated identity is being checked
+ * @param[out] namep
+ *     A printable version of the name of the user
+ * @returns
+ *     True if the user is a super user, or if the server is running
+ *     in noauth mode. Otherwise, false.
+ *
+ * This function is provided for backwards compatibility. New callers
+ * should use the afsconf_SuperIdentity function.
+ */
 
-       /* determine local cell name. It's static, so will only get
-        * calculated the first time through */
-       if (!lcell[0])
-           afsconf_GetLocalCell(adir, lcell, sizeof(lcell));
-
-       /* if running a krb environment, also get the local realm */
-       /* note - this assumes AFS_REALM_SZ <= MAXCELLCHARS */
-       /* just set it to lcell if it fails */
-       if (!lrealm[0]) {
-           if (afs_krb_get_lrealm(lrealm, 0) != 0)     /* KSUCCESS */
-               strncpy(lrealm, lcell, AFS_REALM_SZ);
+afs_int32
+afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall,
+                 char *namep)
+{
+    struct rx_identity *identity;
+    int ret;
+
+    if (namep) {
+       ret = afsconf_SuperIdentity(adir, acall, &identity);
+       if (ret) {
+           if (identity->kind == RX_ID_KRB4) {
+               strlcpy(namep, identity->displayName, MAXKTCNAMELEN-1);
+           } else {
+               snprintf(namep, MAXKTCNAMELEN-1, "eName: %s",
+                        identity->displayName);
+           }
+           rx_identity_free(&identity);
        }
+    } else {
+       ret = afsconf_SuperIdentity(adir, acall, NULL);
+    }
 
+    return ret;
+}
 
-       /* start with no uname and no authorization */
-       strcpy(uname, "");
-       flag = 0;
-
-       /* localauth special case */
-       if (strlen(tinst) == 0 && strlen(tcell) == 0
-           && !strcmp(tname, AUTH_SUPERUSER)) {
-           strcpy(uname, "<LocalAuth>");
-           flag = 1;
-
-           /* cell of connection matches local cell or krb4 realm */
-       } else if (!strcasecmp(tcell, lcell) || !strcasecmp(tcell, lrealm)) {
-           if ((tmp = CompFindUser(adir, tname, ".", tinst, NULL))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#ifdef notyet
-           } else if ((tmp = CompFindUser(adir, tname, "/", tinst, NULL))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#endif
-           }
+/*!
+ * Check whether the user authenticated on a given RX call is
+ * compatible with the access specified by needed_level.
+ *
+ * @param[in] adir
+ *     The configuration directory currently in use
+ * @param[in] acall
+ *     The RX call whose authenticated identity is being checked
+ * @param[in] needed_level
+ *     Either RESTRICTED_QUERY_ANYUSER for allowing any access or
+ *     RESTRICTED_QUERY_ADMIN for allowing super user only.
+ * @returns
+ *     True if the user is compatible with needed_level.
+ *      Otherwise, false.
+ */
 
-           /* cell of conn doesn't match local cell or realm */
-       } else {
-           if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#ifdef notyet
-           } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#endif
-           } else if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell_l))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#ifdef notyet
-           } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell_l))) {
-               strcpy(uname, tmp);
-               flag = 1;
-#endif
-           }
-       }
+int
+afsconf_CheckRestrictedQuery(struct afsconf_dir *adir,
+                            struct rx_call *acall,
+                            int needed_level)
+{
+    if (needed_level == RESTRICTED_QUERY_ANYUSER)
+       return 1;
 
-       if (namep)
-           strcpy(namep, uname);
-       UNLOCK_GLOBAL_MUTEX return flag;
-    } else {                   /* some other auth type */
-       UNLOCK_GLOBAL_MUTEX return 0;   /* mysterious, just say no */
-    }
+    return afsconf_SuperIdentity(adir, acall, NULL);
 }