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