diff --git a/Makefile.in b/Makefile.in index 87b0ec2d34..071501c49f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/configure.ac b/configure.ac index a97fd5d87b..37824e17d1 100644 --- a/configure.ac +++ b/configure.ac @@ -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, diff --git a/src/auth/cellconfig.p.h b/src/auth/cellconfig.p.h index 67750ee2f9..056f7fa1ef 100644 --- a/src/auth/cellconfig.p.h +++ b/src/auth/cellconfig.p.h @@ -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" diff --git a/src/auth/userok.c b/src/auth/userok.c index a8cb274297..3be851e362 100644 --- a/src/auth/userok.c +++ b/src/auth/userok.c @@ -11,6 +11,7 @@ #include #include +#include "base64.h" #include #include @@ -33,6 +34,7 @@ #include #include +#include #include #include #include @@ -46,6 +48,16 @@ #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_DeleteUser(struct afsconf_dir *adir, char *name) +{ + 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; + UserListFileName(dir, tbuffer, sizeof(tbuffer)); + bp = BufioOpen(tbuffer, O_RDONLY, 0); + if (!bp) { + UNLOCK_GLOBAL_MUTEX; + return EIO; + } + while (1) { + code = BufioGets(bp, tbuffer, sizeof(tbuffer)); + if (code < 0) + break; + + 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 (code == 0) { + *identity = rx_identity_copy(&fileUser); + rx_identity_freeContents(&fileUser); + } + + BufioClose(bp); + + UNLOCK_GLOBAL_MUTEX; + 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) { - char tbuffer[256]; - FILE *tf; - char tname[64 + 1]; - char *tp; - int flag; - afs_int32 code; + struct rx_identity *identity; + int 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; + code = GetNthIdentityOrUser(adir, an, &identity, 0); + if (code == 0) { + strlcpy(abuffer, identity->displayName, abufferLen); + rx_identity_free(&identity); } - flag = 1; - while (1) { - /* check for our user id */ - tp = fgets(tbuffer, sizeof(tbuffer), tf); - if (tp == NULL) - break; - code = sscanf(tbuffer, "%64s", tname); - if (code == 1 && an-- == 0) { - flag = 0; - break; - } - } - if (flag == 0) - strcpy(abuffer, tname); - fclose(tf); - UNLOCK_GLOBAL_MUTEX; - return flag; + 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); + if (CompFindUser(adir, tname, ".", tinst, NULL, identity)) { flag = 1; -#ifdef notyet - } else if ((tmp = CompFindUser(adir, tname, "/", tinst, NULL))) { - strcpy(uname, tmp); - 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); + } else if (CompFindUser(adir, tname, ".", tinst, tcell_l, identity)) { 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 } } - 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, ""); + if (identity) + *identity = rx_identity_new(RX_ID_KRB4, "", "", 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; +} diff --git a/tests/Makefile.in b/tests/Makefile.in index 205df8410e..462afcfb06 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -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 diff --git a/tests/TESTS b/tests/TESTS index 03b038e933..8f98f5eebb 100644 --- a/tests/TESTS +++ b/tests/TESTS @@ -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 index 0000000000..89538a6df0 --- /dev/null +++ b/tests/auth/.gitignore @@ -0,0 +1 @@ +/superuser-t diff --git a/tests/auth/Makefile.in b/tests/auth/Makefile.in new file mode 100644 index 0000000000..3c62b266c7 --- /dev/null +++ b/tests/auth/Makefile.in @@ -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 index 0000000000..3232b4306b --- /dev/null +++ b/tests/auth/superuser-t.c @@ -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 +#include + +#include + +#include +#include +#include + +#include + +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; +}