339d190420cb616c8dbae2e4dbf1b75527d7368a
[openafs.git] / tests / auth / superuser-t.c
1 /*
2  * Copyright (c) 2010 Your File System Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 #include <afsconfig.h>
25 #include <afs/param.h>
26
27 #include <roken.h>
28
29 #ifdef HAVE_SYS_WAIT_H
30 #include <sys/wait.h>
31 #endif
32
33 #ifdef IGNORE_SOME_GCC_WARNINGS
34 # pragma GCC diagnostic warning "-Wdeprecated-declarations"
35 #endif
36
37 #include <afs/cellconfig.h>
38 #include <afs/afsutil.h>
39 #include <afs/com_err.h>
40
41 #include <rx/rxkad.h>
42 #include <rx/rx_identity.h>
43
44 #include <tap/basic.h>
45
46 #include "test.h"
47
48 #define TEST_PORT 1234
49
50 static void
51 testOriginalIterator(struct afsconf_dir *dir, int num, char *user) {
52     char buffer[256];
53
54     ok((afsconf_GetNthUser(dir, num, buffer, sizeof buffer) == 0),
55        "User %d successfully returned as %s", num, buffer);
56
57     ok(strcmp(user, buffer) == 0,
58        "User %d matches", num);
59 }
60
61 static void
62 testNewIterator(struct afsconf_dir *dir, int num, struct rx_identity *id) {
63     struct rx_identity *fileId;
64
65     ok((afsconf_GetNthIdentity(dir, num, &fileId) == 0),
66        "Identity %d successfully returned", num);
67
68     ok(rx_identity_match(fileId, id), "Identity %d matches", num);
69
70     rx_identity_free(&fileId);
71 }
72
73 struct rx_securityClass *
74 fakeRXKADClass(struct afsconf_dir *dir,
75                char *name, char *instance, char *realm,
76                afs_uint32 startTime, afs_uint32 endTime)
77 {
78     int code;
79     char buffer[256];
80     struct ktc_encryptionKey key, session;
81     afs_int32 kvno;
82     afs_int32 ticketLen;
83     struct rx_securityClass *class = NULL;
84
85     code = afsconf_GetLatestKey(dir, &kvno, &key);
86     if (code)
87         goto out;
88
89     DES_init_random_number_generator((DES_cblock *) &key);
90     code = DES_new_random_key((DES_cblock *) &session);
91     if (code)
92         goto out;
93
94     ticketLen = sizeof(buffer);
95     memset(buffer, 0, sizeof(buffer));
96     startTime = time(NULL);
97     endTime = startTime + 60 * 60;
98
99     code = tkt_MakeTicket(buffer, &ticketLen, &key, name, instance, realm,
100                           startTime, endTime, &session, 0, "afs", "");
101     if (code)
102         goto out;
103
104     class = rxkad_NewClientSecurityObject(rxkad_clear, &session, kvno,
105                                           ticketLen, buffer);
106 out:
107     return class;
108 }
109
110
111 void
112 startClient(char *configPath)
113 {
114     struct afsconf_dir *dir;
115     struct rx_identity *testId, *anotherId, *extendedId, *dummy;
116     struct rx_securityClass *class;
117     struct rx_connection *conn;
118     afs_uint32 startTime;
119     char ubuffer[256];
120     afs_int32 classIndex;
121     int code;
122     struct hostent *he;
123     afs_uint32 addr;
124     afs_int32 result;
125     char *string;
126
127     plan(63);
128
129     dir = afsconf_Open(configPath);
130     ok(dir!=NULL,
131        "Configuration directory opened sucessfully by client");
132
133     /* Add a normal user to the super user file */
134     ok(afsconf_AddUser(dir, "test") == 0,
135        "Adding a simple user works");
136
137     testId = rx_identity_new(RX_ID_KRB4, "test", "test", strlen("test"));
138
139     /* Check that they are a super user */
140     ok(afsconf_IsSuperIdentity(dir, testId),
141        "User added with old i/face is identitifed as super user");
142
143     /* Check that nobody else is */
144     ok(!afsconf_IsSuperIdentity(dir,
145                                rx_identity_new(RX_ID_KRB4, "testy",
146                                                "testy", strlen("testy"))),
147        "Additional users are not super users");
148
149     ok(afsconf_AddUser(dir, "test") == EEXIST,
150        "Adding a user that already exists fails");
151
152     ok(afsconf_AddIdentity(dir, testId) == EEXIST,
153        "Adding an identity that already exists fails");
154
155     anotherId = rx_identity_new(RX_ID_KRB4, "another",
156                                             "another", strlen("another"));
157
158     /* Add another normal user, but using the extended interface */
159     ok(afsconf_AddIdentity(dir, anotherId) == 0,
160        "Adding a KRB4 identity works");
161
162     /* Check that they are a super user */
163     ok(afsconf_IsSuperIdentity(dir, anotherId),
164        "User added with new i/face is identitifed as super user");
165
166     ok(afsconf_AddIdentity(dir, anotherId) == EEXIST,
167        "Adding a KRB4 identity that already exists fails");
168
169     /* Add an extended user to the super user file */
170     extendedId = rx_identity_new(RX_ID_GSS, "sxw@INF.ED.AC.UK",
171                                  "\x04\x01\x00\x0B\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x00\x00\x00\x10sxw@INF.ED.AC.UK", 35);
172
173     ok(afsconf_AddIdentity(dir, extendedId) == 0,
174        "Adding a GSSAPI identity works");
175
176     /* Check that they are now special */
177     ok(afsconf_IsSuperIdentity(dir, extendedId),
178        "Added GSSAPI identity is a super user");
179
180     /* Check that display name isn't used for matches */
181     ok(!afsconf_IsSuperIdentity(dir,
182                                 rx_identity_new(RX_ID_GSS, "sxw@INF.ED.AC.UK",
183                                                 "abcdefghijklmnopqrstuvwxyz123456789", 35)),
184        "Display name is not used for extended matches");
185
186     ok(afsconf_AddIdentity(dir, extendedId) == EEXIST,
187        "Adding GSSAPI identity twice fails");
188
189     /* Add a final normal user, so we can check that iteration works */
190     /* Add a normal user to the super user file */
191     ok(afsconf_AddUser(dir, "test2") == 0,
192        "Adding another simple user works");
193
194     testOriginalIterator(dir, 0, "test");
195     testOriginalIterator(dir, 1, "another");
196     testOriginalIterator(dir, 2, "test2");
197     ok(afsconf_GetNthUser(dir, 3, ubuffer, sizeof ubuffer) != 0,
198        "Reading past the end of the superuser list fails");
199
200     testNewIterator(dir, 0, testId);
201     testNewIterator(dir, 1, anotherId);
202     testNewIterator(dir, 2, extendedId);
203     testNewIterator(dir, 3, rx_identity_new(RX_ID_KRB4, "test2",
204                                             "test2", strlen("test2")));
205     ok(afsconf_GetNthIdentity(dir, 4, &dummy) != 0,
206        "Reading past the end of the superuser list fails");
207
208     ok(afsconf_DeleteUser(dir, "notthere") != 0,
209        "Deleting a user that doesn't exist fails");
210
211     /* Delete the normal user */
212     ok(afsconf_DeleteUser(dir, "another") == 0,
213        "Deleting normal user works");
214
215     ok(!afsconf_IsSuperIdentity(dir, anotherId),
216        "Deleted user is no longer super user");
217
218     ok(afsconf_IsSuperIdentity(dir, testId) &&
219        afsconf_IsSuperIdentity(dir, extendedId),
220        "Other identities still are");
221
222     ok(afsconf_DeleteIdentity(dir, extendedId) == 0,
223        "Deleting identity works");
224
225     ok(!afsconf_IsSuperIdentity(dir, extendedId),
226        "Deleted identity is no longer special");
227
228     /* Now, what happens if we're doing something over the network instead */
229
230     code = rx_Init(0);
231     is_int(code, 0, "Initialised RX");
232
233     /* Fake up an rx ticket. Note that this will be for the magic 'superuser' */
234     code = afsconf_ClientAuth(dir, &class, &classIndex);
235     is_int(code, 0, "Can successfully create superuser token");
236
237     /* Start a connection to our test service with it */
238     he = gethostbyname("localhost");
239     if (!he) {
240         printf("Couldn't look up server hostname");
241         exit(1);
242     }
243
244     memcpy(&addr, he->h_addr, sizeof(afs_uint32));
245
246     conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID,
247                             class, classIndex);
248
249     /* There's nothing in the list, so this just succeeds because we can */
250     code = TEST_CanI(conn, &result);
251     is_int(0, code, "Can run a simple RPC");
252
253     code = TEST_WhoAmI(conn, &string);
254     is_int(0, code, "Can get identity back");
255     is_string("<LocalAuth>", string, "Forged token is super user");
256
257     /* Throw away this connection and security class */
258     rx_DestroyConnection(conn);
259     rxs_Release(class);
260
261     /* Now fake an rx ticket for a normal user. We have to do more work by hand
262      * here, sadly */
263
264     startTime = time(NULL);
265     class = fakeRXKADClass(dir, "rpctest", "", "", startTime, startTime + 60* 60);
266
267     conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID, class,
268                             RX_SECIDX_KAD);
269
270     code = TEST_CanI(conn, &result);
271     is_int(EPERM, code,
272            "Running RPC as non-super user fails as expected");
273     code = TEST_NewCanI(conn, &result);
274     is_int(EPERM, code,
275            "Running new interface RPC as non-super user fails as expected");
276     code = TEST_WhoAmI(conn, &string);
277     is_int(EPERM, code,
278            "Running RPC returning string fails as expected");
279     code = TEST_NewWhoAmI(conn, &string);
280     is_int(EPERM, code,
281            "Running new interface RPC returning string fails as expected");
282     ok(afsconf_AddUser(dir, "rpctest") == 0,
283        "Adding %s user works", "rpctest");
284     code = TEST_CanI(conn, &result);
285     is_int(0, code, "Running RPC as rpctest works");
286     code = TEST_NewCanI(conn, &result);
287     is_int(0, code, "Running new interface RPC as rpctest works");
288     code = TEST_WhoAmI(conn, &string);
289     is_int(0, code, "Running RPC returning string as %s works", "rpctest");
290     is_string("rpctest", string, "Returned user string matches");
291     code = TEST_NewWhoAmI(conn, &string);
292     is_int(0, code, "Running new RPC returning string as %s works", "rpctest");
293     is_string("rpctest", string, "Returned user string for new interface matches");
294     rx_DestroyConnection(conn);
295     rxs_Release(class);
296
297     /* Now try with an admin principal */
298     startTime = time(NULL);
299     class = fakeRXKADClass(dir, "rpctest", "admin", "", startTime,
300                       startTime + 60* 60);
301
302     conn = rx_NewConnection(addr, htons(TEST_PORT), TEST_SERVICE_ID, class,
303                             RX_SECIDX_KAD);
304
305     code = TEST_CanI(conn, &result);
306     is_int(EPERM, code,
307            "Running RPC as non-super user fails as expected");
308     code = TEST_NewCanI(conn, &result);
309     is_int(EPERM, code,
310            "Running new interface RPC as non-super user fails as expected");
311     code = TEST_WhoAmI(conn, &string);
312     is_int(EPERM, code,
313            "Running RPC returning string fails as expected");
314     code = TEST_NewWhoAmI(conn, &string);
315     is_int(EPERM, code,
316            "Running new interface RPC returning string fails as expected");
317
318     ok(afsconf_AddUser(dir, "rpctest.admin") == 0,
319        "Adding %s user works", "rpctest.admin");
320
321     code = TEST_CanI(conn, &result);
322     is_int(0, code, "Running RPC as %s works", "rpctest/admin");
323     code = TEST_NewCanI(conn, &result);
324     is_int(0, code, "Running new interface RPC as %s works", "rpctest/admin");
325     code = TEST_WhoAmI(conn, &string);
326     is_int(0, code, "Running RPC returning string as %s works", "rpctest/admin");
327     is_string("rpctest.admin", string, "Returned user string matches");
328     code = TEST_NewWhoAmI(conn, &string);
329     is_int(0, code, "Running new interface RPC returning string as %s works",
330            "rpctest/admin");
331     is_string("rpctest.admin", string,
332               "Returned user string from new interface matches");
333
334     rx_DestroyConnection(conn);
335     rxs_Release(class);
336 }
337
338 /**********************************************************************
339  * Server
340  **********************************************************************/
341
342 struct afsconf_dir *globalDir;
343
344 int
345 STEST_CanI(struct rx_call *call, afs_int32 *result)
346 {
347     *result = 0;
348     if (!afsconf_SuperUser(globalDir, call, NULL)) {
349         return EPERM;
350     }
351     return 0;
352 }
353
354 int
355 STEST_NewCanI(struct rx_call *call, afs_int32 *result)
356 {
357     *result = 0;
358     if (!afsconf_SuperIdentity(globalDir, call, NULL)) {
359         return EPERM;
360     }
361     return 0;
362 }
363
364 int
365 STEST_WhoAmI(struct rx_call *call, char **result)
366 {
367    char string[MAXKTCNAMELEN];
368
369    if (!afsconf_SuperUser(globalDir, call, string)) {
370         *result = strdup("");
371         return EPERM;
372    }
373    *result = strdup(string);
374
375    return 0;
376 }
377
378 int
379 STEST_NewWhoAmI(struct rx_call *call, char **result)
380 {
381    struct rx_identity *id;
382
383    if (!afsconf_SuperIdentity(globalDir, call, &id)) {
384         *result = strdup("");
385         return EPERM;
386    }
387    *result = strdup(id->displayName);
388
389    return 0;
390 }
391
392 void
393 startServer(char *configPath)
394 {
395     struct rx_securityClass **classes;
396     afs_int32 numClasses;
397     int code;
398     struct rx_service *service;
399
400     globalDir = afsconf_Open(configPath);
401     if (globalDir == NULL) {
402         fprintf(stderr, "Server: Unable to open config directory\n");
403         exit(1);
404     }
405
406     code = rx_Init(htons(TEST_PORT));
407     if (code != 0) {
408         fprintf(stderr, "Server: Unable to initialise RX\n");
409         exit(1);
410     }
411
412     afsconf_BuildServerSecurityObjects(globalDir, 0, &classes, &numClasses);
413     service = rx_NewService(0, TEST_SERVICE_ID, "test", classes, numClasses,
414                             TEST_ExecuteRequest);
415     if (service == NULL) {
416         fprintf(stderr, "Server: Unable to start to test service\n");
417         exit(1);
418     }
419
420     rx_StartServer(1);
421 }
422
423 int main(int argc, char **argv)
424 {
425     struct afsconf_dir *dir;
426     char buffer[1024];
427     int serverPid, clientPid, waited, stat;
428     char keymaterial[]="\x19\x17\xff\xe6\xbb\x77\x2e\xfc";
429     char *dirEnd;
430     FILE *file;
431     int code;
432
433     /* Start the client and the server if requested */
434
435     if (argc == 3 ) {
436         if (strcmp(argv[1], "-server") == 0) {
437             startServer(argv[2]);
438             exit(0);
439         } else if (strcmp(argv[1], "-client") == 0) {
440             startClient(argv[2]);
441             exit(0);
442         } else {
443             printf("Bad option %s\n", argv[1]);
444             exit(1);
445         }
446     }
447
448     /* Otherwise, do the basic configuration, then start the client and
449      * server */
450
451     snprintf(buffer, sizeof(buffer), "%s/afs_XXXXXX", gettmpdir());
452     mkdtemp(buffer);
453     dirEnd = buffer + strlen(buffer);
454
455     /* Create a CellServDB file */
456     strcpy(dirEnd, "/CellServDB");
457     file = fopen(buffer, "w");
458     fprintf(file, ">example.org # An example cell\n");
459     fprintf(file, "127.0.0.1 #test.example.org\n");
460     fclose(file);
461
462     /* Create a ThisCell file */
463     strcpy(dirEnd, "/ThisCell");
464     file = fopen(buffer, "w");
465     fprintf(file, "example.org\n");
466     fclose(file);
467
468     *dirEnd='\0';
469     /* Start with a blank configuration directory */
470     dir = afsconf_Open(strdup(buffer));
471     if (dir == NULL) {
472         fprintf(stderr, "Unable to configure directory.\n");
473         exit(1);
474     }
475
476     DES_set_odd_parity((DES_cblock *)keymaterial);
477
478     /* Add a key to it so we can use it for connection tests */
479     code = afsconf_AddKey(dir, 1, keymaterial, 1);
480     if (code) {
481         afs_com_err("superuser-t", code, "while adding new key\n");
482         exit(1);
483     }
484
485     printf("Config directory is %s\n", buffer);
486     serverPid = fork();
487     if (serverPid == -1) {
488         /* Bang */
489     } else if (serverPid == 0) {
490         execl(argv[0], argv[0], "-server", buffer, NULL);
491         exit(1);
492     }
493     clientPid = fork();
494     if (clientPid == -1) {
495         kill(serverPid, SIGTERM);
496         waitpid(serverPid, &stat, 0);
497         exit(1);
498     } else if (clientPid == 0) {
499         execl(argv[0], argv[0], "-client", buffer, NULL);
500     }
501
502     do {
503         waited = waitpid(0, &stat, 0);
504     } while(waited == -1 && errno == EINTR);
505
506     if (waited == serverPid) {
507         kill(clientPid, SIGTERM);
508     } else if (waited == clientPid) {
509         kill(serverPid, SIGTERM);
510     }
511     waitpid(0, &stat, 0);
512
513     /* Client and server are both done, so cleanup after everything */
514
515     strcpy(dirEnd, "/KeyFile");
516     unlink(buffer);
517     strcpy(dirEnd, "/CellServDB");
518     unlink(buffer);
519     strcpy(dirEnd, "/ThisCell");
520     unlink(buffer);
521     strcpy(dirEnd, "/UserList");
522     unlink(buffer);
523     *dirEnd='\0';
524     rmdir(buffer);
525
526     return 0;
527 }