#include <afs/param.h>
#include <roken.h>
+#include "base64.h"
#include <afs/stds.h>
#include <afs/pthread_glock.h>
#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)
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
{
/*
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))
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 */
}
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;
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);
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;
/* 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];
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;
}
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;
}
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 */
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;
+}
--- /dev/null
+/*
+ * 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;
+}