From 83ac1558b6cc8245166d997a44297eb197348616 Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Fri, 24 Dec 2010 18:32:30 +0000 Subject: [PATCH] auth: Add more tests and resulting fixes to userok Add tests for the functions afsconf_SuperUser() and afsconf_SuperIdentity(). These had been missing tests because testing them requires starting a client and a server, so amend the superuser-t tests so that they can start up a simple server. Fix a number of problems that the tests expose, with setting (and freeing) identities in corner cases. Change-Id: I29f5f9eda7f532c98183d588e488d704f8efad88 Reviewed-on: http://gerrit.openafs.org/3593 Tested-by: BuildBot Reviewed-by: Derrick Brashear Tested-by: Derrick Brashear --- src/auth/userok.c | 21 +-- tests/auth/.gitignore | 1 + tests/auth/Makefile.in | 22 ++- tests/auth/superuser-t.c | 382 +++++++++++++++++++++++++++++++++++++++++++---- tests/auth/test.xg | 9 ++ 5 files changed, 395 insertions(+), 40 deletions(-) create mode 100644 tests/auth/test.xg diff --git a/src/auth/userok.c b/src/auth/userok.c index 98dbca1..efd488a 100644 --- a/src/auth/userok.c +++ b/src/auth/userok.c @@ -593,7 +593,7 @@ CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst, testId = rx_identity_new(RX_ID_KRB4, fullname, fullname, strlen(fullname)); if (afsconf_IsSuperIdentity(adir, testId)) { - if (*identity) + if (identity) *identity = testId; else rx_identity_free(&testId); @@ -677,8 +677,9 @@ kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst, if ((tinst == NULL || strlen(tinst) == 0) && (tcell == NULL || strlen(tcell) == 0) && !strcmp(tname, AUTH_SUPERUSER)) { - *identity = rx_identity_new(RX_ID_KRB4, AFS_LOCALAUTH_NAME, - AFS_LOCALAUTH_NAME, AFS_LOCALAUTH_LEN); + 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 */ @@ -798,20 +799,22 @@ afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep) { struct rx_identity *identity; - int code; + int ret; - code = afsconf_SuperIdentity(adir, acall, &identity); - if (code) { - if (namep) { + 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); } - rx_identity_free(&identity); + } else { + ret = afsconf_SuperIdentity(adir, acall, NULL); } - return code; + return ret; } diff --git a/tests/auth/.gitignore b/tests/auth/.gitignore index 89538a6..6adae6d 100644 --- a/tests/auth/.gitignore +++ b/tests/auth/.gitignore @@ -1 +1,2 @@ /superuser-t +/test.h diff --git a/tests/auth/Makefile.in b/tests/auth/Makefile.in index 3c62b26..83033a1 100644 --- a/tests/auth/Makefile.in +++ b/tests/auth/Makefile.in @@ -10,13 +10,29 @@ MODULE_CFLAGS=-I$(srcdir)/.. all check test tests: $(TESTS) -superuser-t: superuser-t.o - $(AFS_LDRULE) superuser-t.o ../tap/libtap.a \ +superuser-t: superuser-t.o test.cs.o test.ss.o test.xdr.o + $(AFS_LDRULE) superuser-t.o test.cs.o test.ss.o test.xdr.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) + +test.cs.c: test.xg + $(RXGEN) -A -x -C -o $@ $(srcdir)/test.xg + +test.ss.c: test.xg + $(RXGEN) -A -x -S -o $@ $(srcdir)/test.xg + +test.xdr.c: test.xg + $(RXGEN) -A -x -c -o $@ $(srcdir)/test.xg + +test.h: test.xg + $(RXGEN) -A -x -h -o $@ $(srcdir)/test.xg + +superuser-t.o: test.h + clean: - rm -f *.o superuser-t + rm -f *.o *.cs.c *.ss.c *.xdr.c test.h superuser-t diff --git a/tests/auth/superuser-t.c b/tests/auth/superuser-t.c index 3232b43..6cbc596 100644 --- a/tests/auth/superuser-t.c +++ b/tests/auth/superuser-t.c @@ -26,12 +26,23 @@ #include +#ifdef IGNORE_SOME_GCC_WARNINGS +# pragma GCC diagnostic warning "-Wdeprecated-declarations" +#endif + #include #include +#include + +#include #include #include +#include "test.h" + +#define TEST_PORT 1234 + static void testOriginalIterator(struct afsconf_dir *dir, int num, char *user) { char buffer[256]; @@ -55,39 +66,65 @@ testNewIterator(struct afsconf_dir *dir, int num, struct rx_identity *id) { rx_identity_free(&fileId); } -int main(int argc, char **argv) +struct rx_securityClass * +fakeRXKADClass(struct afsconf_dir *dir, + char *name, char *instance, char *realm, + afs_uint32 startTime, afs_uint32 endTime) { - struct afsconf_dir *dir; - char buffer[1024]; - char ubuffer[256]; - char *dirEnd; - FILE *file; - struct rx_identity *testId, *anotherId, *extendedId, *dummy; - - plan(36); + int code; + char buffer[256]; + struct ktc_encryptionKey key, session; + afs_int32 kvno; + afs_int32 ticketLen; + struct rx_securityClass *class = NULL; + + code = afsconf_GetLatestKey(dir, &kvno, &key); + if (code) + goto out; + + DES_init_random_number_generator((DES_cblock *) &key); + code = DES_new_random_key((DES_cblock *) &session); + if (code) + goto out; + + ticketLen = sizeof(buffer); + memset(buffer, 0, sizeof(buffer)); + startTime = time(NULL); + endTime = startTime + 60 * 60; + + code = tkt_MakeTicket(buffer, &ticketLen, &key, name, instance, realm, + startTime, endTime, &session, 0, "afs", ""); + if (code) + goto out; + + class = rxkad_NewClientSecurityObject(rxkad_clear, &session, kvno, + ticketLen, buffer); +out: + return class; +} - 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); +void +startClient(char *configPath) +{ + struct afsconf_dir *dir; + struct rx_identity *testId, *anotherId, *extendedId, *dummy; + struct rx_securityClass *class; + struct rx_connection *conn; + afs_uint32 startTime; + char ubuffer[256]; + afs_int32 classIndex; + int code; + struct hostent *he; + afs_uint32 addr; + afs_int32 result; + char *string; - /* Create a ThisCell file */ - strcpy(dirEnd, "/ThisCell"); - file = fopen(buffer, "w"); - fprintf(file, "example.org\n"); - fclose(file); + plan(63); - *dirEnd='\0'; - /* Start with a blank configuration directory */ - dir = afsconf_Open(strdup(buffer)); + dir = afsconf_Open(configPath); ok(dir!=NULL, - "Configuration directory opened sucessfully"); + "Configuration directory opened sucessfully by client"); /* Add a normal user to the super user file */ ok(afsconf_AddUser(dir, "test") == 0, @@ -112,7 +149,7 @@ int main(int argc, char **argv) "Adding an identity that already exists fails"); anotherId = rx_identity_new(RX_ID_KRB4, "another", - "another", strlen("another")); + "another", strlen("another")); /* Add another normal user, but using the extended interface */ ok(afsconf_AddIdentity(dir, anotherId) == 0, @@ -184,6 +221,295 @@ int main(int argc, char **argv) ok(!afsconf_IsSuperIdentity(dir, extendedId), "Deleted identity is no longer special"); + /* Now, what happens if we're doing something over the network instead */ + + /* Fake up an rx ticket. Note that this will be for the magic 'superuser' */ + code = afsconf_ClientAuth(dir, &class, &classIndex); + is_int(code, 0, "Can successfully create superuser token"); + + /* Start a connection to our test service with it */ + code = rx_Init(0); + is_int(code, 0, "Started RX"); + + he = gethostbyname("localhost"); + if (!he) { + printf("Couldn't look up server hostname"); + exit(1); + } + + memcpy(&addr, he->h_addr, sizeof(afs_uint32)); + + conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID, + class, classIndex); + + /* There's nothing in the list, so this just succeeds because we can */ + code = TEST_CanI(conn, &result); + is_int(0, code, "Can run a simple RPC"); + + code = TEST_WhoAmI(conn, &string); + is_int(0, code, "Can get identity back"); + is_string("", string, "Forged token is super user"); + + /* Throw away this connection and security class */ + rx_DestroyConnection(conn); + rxs_Release(class); + + /* Now fake an rx ticket for a normal user. We have to do more work by hand + * here, sadly */ + + startTime = time(NULL); + class = fakeRXKADClass(dir, "rpctest", "", "", startTime, startTime + 60* 60); + + conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID, class, + RX_SECIDX_KAD); + + code = TEST_CanI(conn, &result); + is_int(EPERM, code, + "Running RPC as non-super user fails as expected"); + code = TEST_NewCanI(conn, &result); + is_int(EPERM, code, + "Running new interface RPC as non-super user fails as expected"); + code = TEST_WhoAmI(conn, &string); + is_int(EPERM, code, + "Running RPC returning string fails as expected"); + code = TEST_NewWhoAmI(conn, &string); + is_int(EPERM, code, + "Running new interface RPC returning string fails as expected"); + ok(afsconf_AddUser(dir, "rpctest") == 0, + "Adding %s user works", "rpctest"); + code = TEST_CanI(conn, &result); + is_int(0, code, "Running RPC as rpctest works"); + code = TEST_NewCanI(conn, &result); + is_int(0, code, "Running new interface RPC as rpctest works"); + code = TEST_WhoAmI(conn, &string); + is_int(0, code, "Running RPC returning string as %s works", "rpctest"); + is_string("rpctest", string, "Returned user string matches"); + code = TEST_NewWhoAmI(conn, &string); + is_int(0, code, "Running new RPC returning string as %s works", "rpctest"); + is_string("rpctest", string, "Returned user string for new interface matches"); + rx_DestroyConnection(conn); + rxs_Release(class); + + /* Now try with an admin principal */ + startTime = time(NULL); + class = fakeRXKADClass(dir, "rpctest", "admin", "", startTime, + startTime + 60* 60); + + conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID, class, + RX_SECIDX_KAD); + + code = TEST_CanI(conn, &result); + is_int(EPERM, code, + "Running RPC as non-super user fails as expected"); + code = TEST_NewCanI(conn, &result); + is_int(EPERM, code, + "Running new interface RPC as non-super user fails as expected"); + code = TEST_WhoAmI(conn, &string); + is_int(EPERM, code, + "Running RPC returning string fails as expected"); + code = TEST_NewWhoAmI(conn, &string); + is_int(EPERM, code, + "Running new interface RPC returning string fails as expected"); + + ok(afsconf_AddUser(dir, "rpctest.admin") == 0, + "Adding %s user works", "rpctest.admin"); + + code = TEST_CanI(conn, &result); + is_int(0, code, "Running RPC as %s works", "rpctest/admin"); + code = TEST_NewCanI(conn, &result); + is_int(0, code, "Running new interface RPC as %s works", "rpctest/admin"); + code = TEST_WhoAmI(conn, &string); + is_int(0, code, "Running RPC returning string as %s works", "rpctest/admin"); + is_string("rpctest.admin", string, "Returned user string matches"); + code = TEST_NewWhoAmI(conn, &string); + is_int(0, code, "Running new interface RPC returning string as %s works", + "rpctest/admin"); + is_string("rpctest.admin", string, + "Returned user string from new interface matches"); + + rx_DestroyConnection(conn); + rxs_Release(class); +} + +/********************************************************************** + * Server + **********************************************************************/ + +struct afsconf_dir *globalDir; + +int +STEST_CanI(struct rx_call *call, afs_int32 *result) +{ + *result = 0; + if (!afsconf_SuperUser(globalDir, call, NULL)) { + return EPERM; + } + return 0; +} + +int +STEST_NewCanI(struct rx_call *call, afs_int32 *result) +{ + *result = 0; + if (!afsconf_SuperIdentity(globalDir, call, NULL)) { + return EPERM; + } + return 0; +} + +int +STEST_WhoAmI(struct rx_call *call, char **result) +{ + char string[MAXKTCNAMELEN]; + + if (!afsconf_SuperUser(globalDir, call, string)) { + *result = strdup(""); + return EPERM; + } + *result = strdup(string); + + return 0; +} + +int +STEST_NewWhoAmI(struct rx_call *call, char **result) +{ + struct rx_identity *id; + + if (!afsconf_SuperIdentity(globalDir, call, &id)) { + *result = strdup(""); + return EPERM; + } + *result = strdup(id->displayName); + + return 0; +} + +void +startServer(char *configPath) +{ + struct rx_securityClass **classes; + afs_int32 numClasses; + int code; + struct rx_service *service; + + globalDir = afsconf_Open(configPath); + if (globalDir == NULL) { + fprintf(stderr, "Server: Unable to open config directory\n"); + exit(1); + } + + code = rx_Init(htons(TEST_PORT)); + if (code != 0) { + fprintf(stderr, "Server: Unable to initialise RX\n"); + exit(1); + } + + afsconf_BuildServerSecurityObjects(globalDir, 0, &classes, &numClasses); + service = rx_NewService(0, TEST_SERVICE_ID, "test", classes, numClasses, + TEST_ExecuteRequest); + if (service == NULL) { + fprintf(stderr, "Server: Unable to start to test service\n"); + exit(1); + } + + rx_StartServer(1); +} + +int main(int argc, char **argv) +{ + struct afsconf_dir *dir; + char buffer[1024]; + int serverPid, clientPid, waited, stat; + char keymaterial[]="\x19\x17\xff\xe6\xbb\x77\x2e\xfc"; + char *dirEnd; + FILE *file; + int code; + + /* Start the client and the server if requested */ + + if (argc == 3 ) { + if (strcmp(argv[1], "-server") == 0) { + startServer(argv[2]); + exit(0); + } else if (strcmp(argv[1], "-client") == 0) { + startClient(argv[2]); + exit(0); + } else { + printf("Bad option %s\n", argv[1]); + exit(1); + } + } + + /* Otherwise, do the basic configuration, then start the client and + * server */ + + 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)); + if (dir == NULL) { + fprintf(stderr, "Unable to configure directory.\n"); + exit(1); + } + + DES_set_odd_parity((DES_cblock *)keymaterial); + + /* Add a key to it so we can use it for connection tests */ + code = afsconf_AddKey(dir, 1, keymaterial, 1); + if (code) { + afs_com_err("superuser-t", code, "while adding new key\n"); + exit(1); + } + + printf("Config directory is %s\n", buffer); + serverPid = fork(); + if (serverPid == -1) { + /* Bang */ + } else if (serverPid == 0) { + execl(argv[0], argv[0], "-server", buffer, NULL); + exit(1); + } + clientPid = fork(); + if (clientPid == -1) { + kill(serverPid, SIGTERM); + waitpid(serverPid, &stat, 0); + exit(1); + } else if (clientPid == 0) { + execl(argv[0], argv[0], "-client", buffer, NULL); + } + + do { + waited = waitpid(0, &stat, 0); + } while(waited == -1 && errno == EINTR); + + if (waited == serverPid) { + kill(clientPid, SIGTERM); + } else if (waited == clientPid) { + kill(serverPid, SIGTERM); + } + waitpid(0, &stat, 0); + + /* Client and server are both done, so cleanup after everything */ + + strcpy(dirEnd, "/KeyFile"); + unlink(buffer); strcpy(dirEnd, "/CellServDB"); unlink(buffer); strcpy(dirEnd, "/ThisCell"); diff --git a/tests/auth/test.xg b/tests/auth/test.xg new file mode 100644 index 0000000..f7b5c67 --- /dev/null +++ b/tests/auth/test.xg @@ -0,0 +1,9 @@ +package TEST_ +prefix S + +const TEST_SERVICE_ID = 4; + +CanI(OUT int *result) = 1; +WhoAmI(OUT string name<>) = 2; +NewCanI(OUT int *result) = 3; +NewWhoAmI(OUT string name<>) = 4; -- 1.9.4