auth: Allow identities in the UserList
authorSimon Wilkinson <sxw@your-file-system.com>
Wed, 15 Sep 2010 10:17:14 +0000 (11:17 +0100)
committerDerrick Brashear <shadow@dementia.org>
Thu, 25 Nov 2010 03:16:12 +0000 (19:16 -0800)
Extend the userok interface provided by the auth library to permit the
addition, deletion and inspection of identities within the UserList.

A number of additional functions are added, as direct replacements for
their Kerberos v4 only counterparts - these are:
 *) afsconf_DeleteIdentity
 *) afsconf_GetNthIdentity
 *) afsconf_AddIdentity
 *) afsconf_SuperIdentity

In addition, a new function is added to allow the status of any given
identity to be queried
 *) afsconf_IsSuperIdentity

New form identities are stored within the same UserList file as
Kerberos v4 identities. We take advantage of the fact that the current
code skips any entry with a leading whitespace. Identities are stored as
a single line, with a leading space, followed by the integer
representation of their type (0 for Kerberos 4, 1 for GSSAPI), followed
by the base64 encoded representation of their exported name, followed by
the display name of the identity. Each field is whitespace separated.

For example:
 1 BAEACwYJKoZIhvcSAQICAAAAEHN4d0BJTkYuRUQuQUMuVUs= sxw@INF.ED.AC.UK
is the representation of the GSSAPI identity "sxw@INF.ED.AC.UK"

An addition to the test suite is also provided which will test all of
the existing, and new super user manipulation functions.

Change-Id: I50648bb1ecc3037a90d623c87a60193be4f122ff
Reviewed-on: http://gerrit.openafs.org/3355
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementia.org>

Makefile.in
configure.ac
src/auth/cellconfig.p.h
src/auth/userok.c
tests/Makefile.in
tests/TESTS
tests/auth/.gitignore [new file with mode: 0644]
tests/auth/Makefile.in [new file with mode: 0644]
tests/auth/superuser-t.c [new file with mode: 0644]

index 87b0ec2..071501c 100644 (file)
@@ -893,6 +893,7 @@ distclean: clean
        src/tbutc/Makefile \
        src/tests/Makefile \
        src/tests/run-tests \
+       src/tests/auth/Makefile \
        src/tsalvaged/Makefile \
        src/tsm41/Makefile \
        src/tviced/Makefile \
index a97fd5d..37824e1 100644 (file)
@@ -237,6 +237,7 @@ src/volser/Makefile \
 src/xstat/Makefile \
 src/helper-splint.sh \
 tests/Makefile \
+tests/auth/Makefile \
 tests/rpctestlib/Makefile \
 tests/tap/Makefile \
 tests/util/Makefile,
index 67750ee..056f7fa 100644 (file)
@@ -177,15 +177,23 @@ int afsconf_SetCellInfo(struct afsconf_dir *adir, const char *apath,
 /* userok.c */
 
 struct rx_call;
+struct rx_identity;
 extern int afsconf_CheckAuth(void *arock, struct rx_call *acall);
 extern int afsconf_GetNoAuthFlag(struct afsconf_dir *adir);
 extern void afsconf_SetNoAuthFlag(struct afsconf_dir *adir, int aflag);
 extern int afsconf_DeleteUser(struct afsconf_dir *adir, char *auser);
+extern int afsconf_DeleteIdentity(struct afsconf_dir *, struct rx_identity *);
 extern int afsconf_GetNthUser(struct afsconf_dir *adir, afs_int32 an,
                              char *abuffer, afs_int32 abufferLen);
+extern int afsconf_GetNthIdentity(struct afsconf_dir *, int,
+                                 struct rx_identity **);
 extern int afsconf_AddUser(struct afsconf_dir *adir, char *aname);
+extern int afsconf_AddIdentity(struct afsconf_dir *adir, struct rx_identity *);
 extern int afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall,
                              char *namep);
+extern int afsconf_SuperIdentity(struct afsconf_dir *, struct rx_call *,
+                                struct rx_identity **);
+extern int afsconf_IsSuperIdentity(struct afsconf_dir *, struct rx_identity *);
 
 /* some well-known ports and their names; new additions to table in cellconfig.c, too */
 #define        AFSCONF_FILESERVICE             "afs"
index a8cb274..3be851e 100644 (file)
@@ -11,6 +11,7 @@
 #include <afs/param.h>
 
 #include <roken.h>
+#include "base64.h"
 
 #include <afs/stds.h>
 #include <afs/pthread_glock.h>
@@ -33,6 +34,7 @@
 
 #include <rx/xdr.h>
 #include <rx/rx.h>
+#include <rx/rx_identity.h>
 #include <stdio.h>
 #include <afs/afsutil.h>
 #include <afs/fileutil.h>
 #include "keys.h"
 #include "afs/audit.h"
 
+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, NULL);
+}
+
 #if !defined(UKERNEL)
 int
 afsconf_CheckAuth(void *arock, struct rx_call *acall)
@@ -105,24 +117,42 @@ afsconf_SetNoAuthFlag(struct afsconf_dir *adir, int aflag)
     UNLOCK_GLOBAL_MUTEX;
 }
 
-/* deletes a user from the UserList file */
+/*!
+ * 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
+ */
+
 int
-afsconf_DeleteUser(struct afsconf_dir *adir, char *auser)
+afsconf_DeleteIdentity(struct afsconf_dir *adir, struct rx_identity *user)
 {
     char tbuffer[1024];
     char nbuffer[1024];
+    char *copy;
     FILE *tf;
     FILE *nf;
     int flag;
-    char tname[64 + 1];
     char *tp;
     int found;
     struct stat tstat;
+    struct rx_identity identity;
     afs_int32 code;
 
     LOCK_GLOBAL_MUTEX;
-    strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
-              AFSDIR_ULIST_FILE, NULL);
+    UserListFileName(adir, tbuffer, sizeof tbuffer);
 #ifndef AFS_NT40_ENV
     {
        /*
@@ -162,14 +192,22 @@ afsconf_DeleteUser(struct afsconf_dir *adir, char *auser)
        tp = fgets(nbuffer, sizeof(nbuffer), tf);
        if (tp == NULL)
            break;
-       code = sscanf(nbuffer, "%64s", tname);
-       if (code == 1 && strcmp(tname, auser) == 0) {
+
+       copy = strdup(nbuffer);
+       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 */
+           /* otherwise copy original line to output */
            fprintf(nf, "%s", nbuffer);
        }
+       free(copy);
+       rx_identity_freeContents(&identity);
     }
     fclose(tf);
     if (ferror(nf))
@@ -195,87 +233,278 @@ afsconf_DeleteUser(struct afsconf_dir *adir, char *auser)
     return 0;                  /* everything was fine */
 }
 
-/* returns nth super user from the UserList file */
+/*!
+ * 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_GetNthUser(struct afsconf_dir *adir, afs_int32 an, char *abuffer,
-                  afs_int32 abufferLen)
+afsconf_DeleteUser(struct afsconf_dir *adir, char *name)
 {
-    char tbuffer[256];
-    FILE *tf;
-    char tname[64 + 1];
-    char *tp;
-    int flag;
+    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
+ */
+static int
+GetNthIdentityOrUser(struct afsconf_dir *dir, int count,
+                    struct rx_identity **identity, int id)
+{
+    bufio_p bp;
+    char tbuffer[1024];
+    struct rx_identity fileUser;
     afs_int32 code;
 
     LOCK_GLOBAL_MUTEX;
-    strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
-              AFSDIR_ULIST_FILE, NULL);
-    tf = fopen(tbuffer, "r");
-    if (!tf) {
+    UserListFileName(dir, tbuffer, sizeof(tbuffer));
+    bp = BufioOpen(tbuffer, O_RDONLY, 0);
+    if (!bp) {
        UNLOCK_GLOBAL_MUTEX;
-       return 1;
+       return EIO;
     }
-    flag = 1;
     while (1) {
-       /* check for our user id */
-       tp = fgets(tbuffer, sizeof(tbuffer), tf);
-       if (tp == NULL)
+       code = BufioGets(bp, tbuffer, sizeof(tbuffer));
+       if (code < 0)
            break;
-       code = sscanf(tbuffer, "%64s", tname);
-       if (code == 1 && an-- == 0) {
-           flag = 0;
+
+       code = ParseLine(tbuffer, &fileUser);
+       if (code != 0)
            break;
-       }
+
+       if (id || fileUser.kind == RX_ID_KRB4)
+           count--;
+
+       if (count < 0)
+           break;
+        else
+           rx_identity_freeContents(&fileUser);
     }
-    if (flag == 0)
-       strcpy(abuffer, tname);
-    fclose(tf);
+    if (code == 0) {
+       *identity = rx_identity_copy(&fileUser);
+       rx_identity_freeContents(&fileUser);
+    }
+
+    BufioClose(bp);
+
     UNLOCK_GLOBAL_MUTEX;
-    return flag;
+    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
+ *     0 on success, non-zero on failure
+ */
+
+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
+ *     0 on success, non-zero on failure
+ *
+ * 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.
+ */
+
+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);
+    }
+    return code;
 }
 
-/* returns true iff user is in the UserList file */
+/*!
+ * 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
-FindUser(struct afsconf_dir *adir, char *auser)
+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 ! */
+    }
+
+    /* 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;
+}
+
+/*!
+ * 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];
     bufio_p bp;
-    char tname[64 + 1];
-    int flag;
+    char tbuffer[1024];
+    struct rx_identity fileUser;
+    int match;
     afs_int32 code;
-    int rc;
 
     strcompose(tbuffer, sizeof tbuffer, adir->name, "/", AFSDIR_ULIST_FILE,
               NULL);
     bp = BufioOpen(tbuffer, O_RDONLY, 0);
     if (!bp)
        return 0;
-    flag = 0;
-    while (1) {
-       /* check for our user id */
-       rc = BufioGets(bp, tbuffer, sizeof(tbuffer));
-       if (rc < 0)
+    match = 0;
+    while (!match) {
+       code = BufioGets(bp, tbuffer, sizeof(tbuffer));
+        if (code < 0)
            break;
-       code = sscanf(tbuffer, "%64s", tname);
-       if (code == 1 && strcmp(tname, auser) == 0) {
-           flag = 1;
-           break;
-       }
+
+       code = ParseLine(tbuffer, &fileUser);
+       if (code != 0)
+          break;
+
+       match = rx_identity_match(user, &fileUser);
+
+       rx_identity_freeContents(&fileUser);
     }
     BufioClose(bp);
-    return flag;
+    return match;
 }
 
 /* add a user to the user list, checking for duplicates */
 int
-afsconf_AddUser(struct afsconf_dir *adir, char *aname)
+afsconf_AddIdentity(struct afsconf_dir *adir, struct rx_identity *user)
 {
     FILE *tf;
     afs_int32 code;
+    char *ename;
     char tbuffer[256];
 
     LOCK_GLOBAL_MUTEX;
-    if (FindUser(adir, aname)) {
+    if (afsconf_IsSuperIdentity(adir, user)) {
        UNLOCK_GLOBAL_MUTEX;
        return EEXIST;          /* already in the list */
     }
@@ -287,7 +516,14 @@ afsconf_AddUser(struct afsconf_dir *adir, char *aname)
        UNLOCK_GLOBAL_MUTEX;
        return EIO;
     }
-    fprintf(tf, "%s\n", aname);
+    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);
+    }
     code = 0;
     if (ferror(tf))
        code = EIO;
@@ -297,26 +533,44 @@ afsconf_AddUser(struct afsconf_dir *adir, char *aname)
     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,
        otherwise returns NULL. The resulting string should be immediately
        copied to other storage prior to release of mutex. */
-static char *
+static int
 CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst,
-            char *realm)
+            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);
 
     /* might have instance */
     if (inst && inst[0]) {
        if (!sep || !sep[0]) {
-           return NULL;
+           return 0;
        }
 
        strcat(fullname, sep);
@@ -329,16 +583,22 @@ CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst,
        strcat(fullname, realm);
     }
 
-    if (FindUser(adir, fullname)) {
-       return fullname;
-    } else {
-       return NULL;
+    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;
     }
+
+    rx_identity_free(&testId);
+    return 0;
 }
 
 static int
 kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst,
-                 char *tcell, char *namep)
+                 char *tcell, struct rx_identity **identity)
 {
     char tcell_l[MAXKTCREALMLEN] = "";
     char *tmp;
@@ -418,44 +678,24 @@ kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst,
 
        /* cell of connection matches local cell or one of the realms */
     } else if (!strcasecmp(tcell, lcell) || lrealm_match) {
-       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);
+       if (CompFindUser(adir, tname, ".", tinst, NULL, identity)) {
            flag = 1;
-#endif
        }
        /* cell of conn doesn't match local cell or realm */
     } else {
-       if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell))) {
-           strcpy(uname, tmp);
+       if (CompFindUser(adir, tname, ".", tinst, tcell, identity)) {
            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);
+       } else if (CompFindUser(adir, tname, ".", tinst, tcell_l, identity)) {
            flag = 1;
-#ifdef notyet
-       } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell_l))) {
-           strcpy(uname, tmp);
-           flag = 1;
-#endif
        }
     }
 
-    if (namep)
-       strcpy(namep, uname);
-
     return flag;
 }
 
 static int
-rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
+rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall,
+              struct rx_identity **identity)
 {
     char tname[MAXKTCNAMELEN]; /* authentication from ticket */
     char tinst[MAXKTCNAMELEN];
@@ -470,15 +710,27 @@ rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
     if (code)
        return 0;               /* bogus connection/other error */
 
-    return kerberosSuperUser(adir, tname, tinst, tcell, namep);
+    return kerberosSuperUser(adir, tname, tinst, tcell, identity);
 }
 
-/* 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.
-*/
+/*!
+ * 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(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
+afsconf_SuperIdentity(struct afsconf_dir *adir, struct rx_call *acall,
+                     struct rx_identity **identity)
 {
     struct rx_connection *tconn;
     afs_int32 code;
@@ -491,8 +743,8 @@ afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
     }
 
     if (afsconf_GetNoAuthFlag(adir)) {
-       if (namep)
-           strcpy(namep, "<NoAuth>");
+       if (identity)
+           *identity = rx_identity_new(RX_ID_KRB4, "<NoAuth>", "<NoAuth>", 8);
        UNLOCK_GLOBAL_MUTEX;
        return 1;
     }
@@ -507,7 +759,7 @@ afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
        UNLOCK_GLOBAL_MUTEX;
        return 0;               /* not supported any longer */
     } else if (code == 2) {
-       flag = rxkadSuperUser(adir, acall, namep);
+       flag = rxkadSuperUser(adir, acall, identity);
        UNLOCK_GLOBAL_MUTEX;
        return flag;
     } else {                   /* some other auth type */
@@ -515,3 +767,43 @@ afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
        return 0;               /* mysterious, just say no */
     }
 }
+
+/*!
+ * 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.
+ */
+
+afs_int32
+afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall,
+                 char *namep)
+{
+    struct rx_identity *identity;
+    int code;
+
+    code = afsconf_SuperIdentity(adir, acall, &identity);
+    if (namep) {
+       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);
+
+    return code;
+}
index 205df84..462afcf 100644 (file)
@@ -11,6 +11,8 @@ MODULE_CFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \
 
 all: runtests
        cd tap && $(MAKE) $@
+       cd auth && $(MAKE) $@
+       cd rxgk && $(MAKE) $@
        cd util && $(MAKE) $@
 
 runtests: runtests.o
@@ -18,6 +20,7 @@ runtests: runtests.o
 
 check test tests: runtests
        cd tap && $(MAKE) $@
+       cd auth && $(MAKE) $@
        cd util && $(MAKE) $@
        ./runtests $(abs_top_srcdir)/tests/TESTS
 
@@ -25,5 +28,6 @@ install:
 
 clean distclean:
        cd tap && $(MAKE) $@
+       cd auth && $(MAKE) $@
        cd util && $(MAKE) $@
        $(RM) -f *.o core runtests
index 03b038e..8f98f5e 100644 (file)
@@ -1,2 +1,3 @@
 util/ktime
 util/exec-alt
+auth/superuser
diff --git a/tests/auth/.gitignore b/tests/auth/.gitignore
new file mode 100644 (file)
index 0000000..89538a6
--- /dev/null
@@ -0,0 +1 @@
+/superuser-t
diff --git a/tests/auth/Makefile.in b/tests/auth/Makefile.in
new file mode 100644 (file)
index 0000000..3c62b26
--- /dev/null
@@ -0,0 +1,22 @@
+
+srcdir=@srcdir@
+abs_top_builddir=@abs_top_builddir@
+include @TOP_OBJDIR@/src/config/Makefile.config
+include @TOP_OBJDIR@/src/config/Makefile.pthread
+
+TESTS = superuser-t
+
+MODULE_CFLAGS=-I$(srcdir)/..
+
+all check test tests: $(TESTS)
+
+superuser-t: superuser-t.o
+       $(AFS_LDRULE) superuser-t.o ../tap/libtap.a \
+               $(abs_top_builddir)/lib/libafsauthent.a \
+               $(abs_top_builddir)/lib/librxgk.a \
+               $(abs_top_builddir)/lib/libafsrpc.a \
+               $(abs_top_builddir)/lib/libafshcrypto.a \
+               $(LIB_rfc3961) $(LIB_roken) -lafsutil\
+               $(XLIBS)
+clean:
+       rm -f *.o superuser-t
diff --git a/tests/auth/superuser-t.c b/tests/auth/superuser-t.c
new file mode 100644 (file)
index 0000000..3232b43
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2010 Your File System Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#include <roken.h>
+
+#include <afs/cellconfig.h>
+#include <afs/afsutil.h>
+#include <rx/rx_identity.h>
+
+#include <tap/basic.h>
+
+static void
+testOriginalIterator(struct afsconf_dir *dir, int num, char *user) {
+    char buffer[256];
+
+    ok((afsconf_GetNthUser(dir, num, buffer, sizeof buffer) == 0),
+       "User %d successfully returned as %s", num, buffer);
+
+    ok(strcmp(user, buffer) == 0,
+       "User %d matches", num);
+}
+
+static void
+testNewIterator(struct afsconf_dir *dir, int num, struct rx_identity *id) {
+    struct rx_identity *fileId;
+
+    ok((afsconf_GetNthIdentity(dir, num, &fileId) == 0),
+       "Identity %d successfully returned", num);
+
+    ok(rx_identity_match(fileId, id), "Identity %d matches", num);
+
+    rx_identity_free(&fileId);
+}
+
+int main(int argc, char **argv)
+{
+    struct afsconf_dir *dir;
+    char buffer[1024];
+    char ubuffer[256];
+    char *dirEnd;
+    FILE *file;
+    struct rx_identity *testId, *anotherId, *extendedId, *dummy;
+
+    plan(36);
+
+    snprintf(buffer, sizeof(buffer), "%s/afs_XXXXXX", gettmpdir());
+    mkdtemp(buffer);
+    dirEnd = buffer + strlen(buffer);
+
+    /* Create a CellServDB file */
+    strcpy(dirEnd, "/CellServDB");
+    file = fopen(buffer, "w");
+    fprintf(file, ">example.org # An example cell\n");
+    fprintf(file, "127.0.0.1 #test.example.org\n");
+    fclose(file);
+
+    /* Create a ThisCell file */
+    strcpy(dirEnd, "/ThisCell");
+    file = fopen(buffer, "w");
+    fprintf(file, "example.org\n");
+    fclose(file);
+
+    *dirEnd='\0';
+    /* Start with a blank configuration directory */
+    dir = afsconf_Open(strdup(buffer));
+    ok(dir!=NULL,
+       "Configuration directory opened sucessfully");
+
+    /* Add a normal user to the super user file */
+    ok(afsconf_AddUser(dir, "test") == 0,
+       "Adding a simple user works");
+
+    testId = rx_identity_new(RX_ID_KRB4, "test", "test", strlen("test"));
+
+    /* Check that they are a super user */
+    ok(afsconf_IsSuperIdentity(dir, testId),
+       "User added with old i/face is identitifed as super user");
+
+    /* Check that nobody else is */
+    ok(!afsconf_IsSuperIdentity(dir,
+                              rx_identity_new(RX_ID_KRB4, "testy",
+                                              "testy", strlen("testy"))),
+       "Additional users are not super users");
+
+    ok(afsconf_AddUser(dir, "test") == EEXIST,
+       "Adding a user that already exists fails");
+
+    ok(afsconf_AddIdentity(dir, testId) == EEXIST,
+       "Adding an identity that already exists fails");
+
+    anotherId = rx_identity_new(RX_ID_KRB4, "another",
+                                          "another", strlen("another"));
+
+    /* Add another normal user, but using the extended interface */
+    ok(afsconf_AddIdentity(dir, anotherId) == 0,
+       "Adding a KRB4 identity works");
+
+    /* Check that they are a super user */
+    ok(afsconf_IsSuperIdentity(dir, anotherId),
+       "User added with new i/face is identitifed as super user");
+
+    ok(afsconf_AddIdentity(dir, anotherId) == EEXIST,
+       "Adding a KRB4 identity that already exists fails");
+
+    /* Add an extended user to the super user file */
+    extendedId = rx_identity_new(RX_ID_GSS, "sxw@INF.ED.AC.UK",
+                                "\x04\x01\x00\x0B\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x00\x00\x00\x10sxw@INF.ED.AC.UK", 35);
+
+    ok(afsconf_AddIdentity(dir, extendedId) == 0,
+       "Adding a GSSAPI identity works");
+
+    /* Check that they are now special */
+    ok(afsconf_IsSuperIdentity(dir, extendedId),
+       "Added GSSAPI identity is a super user");
+
+    /* Check that display name isn't used for matches */
+    ok(!afsconf_IsSuperIdentity(dir,
+                               rx_identity_new(RX_ID_GSS, "sxw@INF.ED.AC.UK",
+                                               "abcdefghijklmnopqrstuvwxyz123456789", 35)),
+       "Display name is not used for extended matches");
+
+    ok(afsconf_AddIdentity(dir, extendedId) == EEXIST,
+       "Adding GSSAPI identity twice fails");
+
+    /* Add a final normal user, so we can check that iteration works */
+    /* Add a normal user to the super user file */
+    ok(afsconf_AddUser(dir, "test2") == 0,
+       "Adding another simple user works");
+
+    testOriginalIterator(dir, 0, "test");
+    testOriginalIterator(dir, 1, "another");
+    testOriginalIterator(dir, 2, "test2");
+    ok(afsconf_GetNthUser(dir, 3, ubuffer, sizeof ubuffer) != 0,
+       "Reading past the end of the superuser list fails");
+
+    testNewIterator(dir, 0, testId);
+    testNewIterator(dir, 1, anotherId);
+    testNewIterator(dir, 2, extendedId);
+    testNewIterator(dir, 3, rx_identity_new(RX_ID_KRB4, "test2",
+                                           "test2", strlen("test2")));
+    ok(afsconf_GetNthIdentity(dir, 4, &dummy) != 0,
+       "Reading past the end of the superuser list fails");
+
+    ok(afsconf_DeleteUser(dir, "notthere") != 0,
+       "Deleting a user that doesn't exist fails");
+
+    /* Delete the normal user */
+    ok(afsconf_DeleteUser(dir, "another") == 0,
+       "Deleting normal user works");
+
+    ok(!afsconf_IsSuperIdentity(dir, anotherId),
+       "Deleted user is no longer super user");
+
+    ok(afsconf_IsSuperIdentity(dir, testId) &&
+       afsconf_IsSuperIdentity(dir, extendedId),
+       "Other identities still are");
+
+    ok(afsconf_DeleteIdentity(dir, extendedId) == 0,
+       "Deleting identity works");
+
+    ok(!afsconf_IsSuperIdentity(dir, extendedId),
+       "Deleted identity is no longer special");
+
+    strcpy(dirEnd, "/CellServDB");
+    unlink(buffer);
+    strcpy(dirEnd, "/ThisCell");
+    unlink(buffer);
+    strcpy(dirEnd, "/UserList");
+    unlink(buffer);
+    *dirEnd='\0';
+    rmdir(buffer);
+
+    return 0;
+}