reindent-20030715
[openafs.git] / src / libacl / aclprocs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11         Information Technology Center
12         Carnegie-Mellon University
13 */
14
15
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 RCSID
20     ("$Header$");
21
22 #include <sys/types.h>
23 #ifdef AFS_NT40_ENV
24 #include <winsock2.h>
25 #else
26 #include <netinet/in.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 #include <rx/xdr.h>
32 #include <rx/rx.h>
33 #include <afs/ptclient.h>
34 #include "acl.h"
35
36 #ifdef AFS_PTHREAD_ENV
37 #include <assert.h>
38 #include <pthread.h>
39 pthread_mutex_t acl_list_mutex;
40 #endif /* AFS_PTHREAD_ENV */
41
42 static int AddToList();
43 static int GetFromList();
44
45 struct freeListEntry {
46     struct freeListEntry *next;
47     int size;
48     char body[1];
49 };
50
51 struct freeListEntry *freeList;
52
53 /*todo: for sorting acls - make sure they work with new groups lists 10/5*/
54 static int
55 CmpPlus(a, b)
56      struct acl_accessEntry *a, *b;
57 {
58     if (a->id < b->id)
59         return (-1);
60     if (a->id == b->id)
61         return (0);
62     return (1);
63 }
64
65 static int
66 CmpMinus(a, b)
67      struct acl_accessEntry *a, *b;
68 {
69     if (a->id > b->id)
70         return (-1);
71     if (a->id == b->id)
72         return (0);
73     return (1);
74 }
75
76 static int
77 CmpInt(x, y)
78      afs_int32 x, y;
79 {
80     if (x < y)
81         return (-1);
82     if (x == y)
83         return (0);
84     return (1);
85 }
86
87
88 int
89 acl_NewACL(nEntries, acl)
90      int nEntries;
91      struct acl_accessList **acl;
92 {
93     /* Creates an access list capable of holding at least nEntries entries.
94      * Returns 0 on success; aborts if we run out of memory. */
95
96     int t;
97     struct freeListEntry *e;
98
99     t = sizeof(struct acl_accessList) + (nEntries -
100                                          1) * sizeof(struct acl_accessEntry);
101     if (GetFromList(&freeList, &e, t) < 0) {
102         e = (struct freeListEntry *)malloc(t + sizeof(int) +
103                                            sizeof(struct freeListEntry *));
104         if (e == NULL) {
105             perror("acl_NewACL: malloc() failed");
106             abort();
107         }
108         e->size = t;
109         *acl = (struct acl_accessList *)(e->body);
110     } else
111         *acl = (struct acl_accessList *)(e->body);
112
113     (*acl)->size = t;           /* May be less than actual size of storage */
114     (*acl)->version = ACL_ACLVERSION;
115     (*acl)->total = nEntries;
116     (*acl)->positive = (*acl)->negative = 0;
117     return (0);
118 }
119
120
121 int
122 acl_FreeACL(acl)
123      struct acl_accessList **acl;
124 {
125     /* Releases the access list defined by acl.  Returns 0 always. */
126     struct freeListEntry *x;
127
128     x = (struct freeListEntry *)
129         ((char *)*acl - sizeof(struct freeListEntry *) - sizeof(int));
130     *acl = NULL;
131     return (AddToList(&freeList, x));
132 }
133
134 int
135 acl_NewExternalACL(nEntries, r)
136      int nEntries;
137      char **r;
138 {
139     /* Puts an external acl big enough to hold nEntries in r.  Returns 0 on success, aborts if insufficient memory. */
140
141     int t;
142     struct freeListEntry *e;
143
144     t = 20 + (nEntries) * (PR_MAXNAMELEN + 20);
145     /* Conservative estimate: enough space in each entry for longest 
146      * name plus decimal 2**32 (for largest rights mask) plus some formatting */
147
148     if (GetFromList(&freeList, &e, t)) {
149         e = (struct freeListEntry *)malloc(t + sizeof(int) +
150                                            sizeof(struct freeListEntry *));
151         if (e == NULL) {
152             perror("acl_NewExternalACL(): malloc() failed");
153             abort();
154         }
155         e->size = t;
156     }
157
158     *r = e->body;
159     sprintf(*r, "0\n0\n");
160     return (0);
161 }
162
163 int
164 acl_FreeExternalACL(r)
165      char **r;
166 {
167     /* Releases the external access list defined by r.  Returns 0 always.  */
168
169     struct freeListEntry *x;
170
171     x = (struct freeListEntry *)
172         ((char *)*r - sizeof(struct freeListEntry *) - sizeof(int));
173     *r = NULL;
174     return (AddToList(&freeList, x));
175 }
176
177
178 int
179 acl_Externalize(acl, elist)
180      struct acl_accessList *acl;
181      char **elist;
182 {
183     /* Converts the access list defined by acl into the external access list in elist.  Non-translatable id's are converted to their ASCII string representations.  Returns 0 on success, -1 if number of entries exceeds ACL_MAXENTRIES, or a failure code from the protection server if the problem occured there. */
184
185     register int i;
186     register int j;
187     register code;
188     register char *nextc;
189     idlist lids;
190     namelist lnames;
191
192     if (acl->total > ACL_MAXENTRIES)
193         return (-1);
194     acl_NewExternalACL(acl->total, elist);
195     nextc = *elist;
196     lids.idlist_val =
197         (afs_int32 *) malloc(ACL_MAXENTRIES * sizeof(afs_int32));
198     memset(lids.idlist_val, 0, ACL_MAXENTRIES * sizeof(afs_int32));
199     lids.idlist_len = acl->total;
200     lnames.namelist_len = 0;
201     lnames.namelist_val = (prname *) 0;
202     sprintf(nextc, "%d\n%d\n", acl->positive, acl->negative);
203     nextc += strlen(nextc);
204     for (i = 0; i < acl->positive; i++)
205         lids.idlist_val[i] = acl->entries[i].id;
206     j = i;
207     for (i = acl->total - 1; i >= acl->total - acl->negative; i--, j++)
208         lids.idlist_val[j] = acl->entries[i].id;
209     code = pr_IdToName(&lids, &lnames);
210     if (code != 0) {
211         if (lids.idlist_val)
212             free(lids.idlist_val);
213         if (lnames.namelist_val)
214             free(lnames.namelist_val);
215         return code;
216     }
217     for (i = 0; i < acl->positive; i++) {
218         sprintf(nextc, "%s", lnames.namelist_val[i]);
219         nextc += strlen(nextc);
220         sprintf(nextc, "\t%d\n", acl->entries[i].rights);
221         nextc += strlen(nextc);
222     }
223     j = i;
224     for (i = acl->total - 1; i >= acl->total - acl->negative; i--, j++) {
225         sprintf(nextc, "%s", lnames.namelist_val[j]);
226         nextc += strlen(nextc);
227         sprintf(nextc, "\t%d\n", acl->entries[i].rights);
228         nextc += strlen(nextc);
229     }
230     if (lids.idlist_val)
231         free(lids.idlist_val);
232     if (lnames.namelist_val)
233         free(lnames.namelist_val);
234     return (0);
235 }
236
237
238 int
239 acl_Internalize(elist, acl)
240      char *elist;
241      struct acl_accessList **acl;
242 {
243     /* Converts the external access list elist into the access list acl.  Returns 0 on success, -1 if ANY name is not translatable, or if the number of entries exceeds al_maxExtEntries. */
244     register int i;
245     register int j;
246     register char *nextc;
247     register afs_int32 code;
248     int p, n;
249     namelist lnames;
250     idlist lids;
251
252     if (sscanf(elist, "%d\n%d\n", &p, &n) != 2)
253         return -1;
254     if (p + n > ACL_MAXENTRIES)
255         return (-1);
256     acl_NewACL(p + n, acl);
257     (*acl)->total = p + n;
258     (*acl)->positive = p;
259     (*acl)->negative = n;
260     if ((*acl)->total == 0) {
261         /* Empty acl entry; simply return success */
262         return 0;
263     }
264     lnames.namelist_len = (*acl)->total;
265     lnames.namelist_val =
266         (prname *) malloc(lnames.namelist_len * PR_MAXNAMELEN);
267     if (lnames.namelist_val == 0) {
268         return -1;
269     }
270     nextc = elist;
271     while (*nextc && *nextc != '\n')
272         nextc++;
273     nextc++;
274     while (*nextc && *nextc != '\n')
275         nextc++;
276     nextc++;                    /* now at the beginning of the entry list */
277     for (i = 0; i < (*acl)->positive; i++) {
278         int k;
279         if (sscanf(nextc, "%s\t%d\n", lnames.namelist_val[i], &k) != 2)
280             return (-1);
281         (*acl)->entries[i].rights = k;
282         nextc = strchr(nextc, '\n');
283         nextc++;                /* 1 + index can cast ptr to integer */
284     }
285     j = i;
286     for (i = (*acl)->total - 1; i >= (*acl)->total - (*acl)->negative;
287          i--, j++) {
288         if (sscanf
289             (nextc, "%s\t%d\n", lnames.namelist_val[j],
290              &((*acl)->entries[j].rights)) != 2)
291             return (-1);
292         nextc = strchr(nextc, '\n');
293         nextc++;
294     }
295     lids.idlist_len = 0;
296     lids.idlist_val = 0;
297
298     code = pr_NameToId(&lnames, &lids);
299     if (code) {
300         free(lnames.namelist_val);
301         if (lids.idlist_val)
302             free(lids.idlist_val);
303         return -1;
304     }
305     for (i = 0; i < (*acl)->positive; i++) {
306         if (lids.idlist_val[i] == ANONYMOUSID) {
307             free(lnames.namelist_val);
308             if (lids.idlist_val)
309                 free(lids.idlist_val);
310             return -1;
311         }
312         (*acl)->entries[i].id = lids.idlist_val[i];
313     }
314     j = i;
315     for (i = (*acl)->total - 1; i >= (*acl)->total - (*acl)->negative; i--) {
316         if (lids.idlist_val[i] == ANONYMOUSID) {
317             free(lnames.namelist_val);
318             if (lids.idlist_val)
319                 free(lids.idlist_val);
320             return -1;
321         }
322         (*acl)->entries[i].id = lids.idlist_val[i];
323     }
324     /* sort for easier lookup */
325     qsort(&((*acl)->entries[0]), (*acl)->positive,
326           sizeof(struct acl_accessEntry), CmpPlus);
327     qsort(&((*acl)->entries[(*acl)->total - (*acl)->negative]),
328           (*acl)->negative, sizeof(struct acl_accessEntry), CmpMinus);
329     free(lnames.namelist_val);
330     if (lids.idlist_val)
331         free(lids.idlist_val);
332     return (0);
333 }
334
335
336 int
337 acl_CheckRights(acl, groups, rights)
338      struct acl_accessList *acl;
339      prlist *groups;
340      int *rights;
341 {
342     /* Returns the rights given by acl to groups */
343
344     int temprights;             /* positive rights accumulated so far */
345     int negrights;              /* negative rights accumulated so far */
346     int a;                      /* index into next entry in acl */
347     int c;                      /* index into next entry in CPS */
348
349     /* more sanity checks */
350     if (acl->total > ACL_MAXENTRIES)
351         return 1;
352     if (acl->total < 0)
353         return 1;
354     if (acl->size > 192)
355         return 1;               /* 192 is the room in a 256 byte vnode reserved for the ACL */
356
357     if (acl->total <= 0 || groups->prlist_len <= 0) {
358         *rights = 0;
359         return (0);
360     }
361     if (groups->prlist_val[groups->prlist_len - 1] == SYSADMINID) {
362         *rights = -1;
363         return 0;
364     }
365
366     /* Each iteration eats up exactly one entry from either acl or groups.
367      * Duplicate Entries in access list ==> accumulated rights are obtained.
368      * Duplicate Entries in groups ==> irrelevant */
369     temprights = 0;
370     c = a = 0;
371     while ((a < acl->positive) && (c < groups->prlist_len))
372         switch (CmpInt(acl->entries[a].id, groups->prlist_val[c])) {
373         case -1:
374             a += 1;
375             break;
376
377         case 0:
378             temprights |= acl->entries[a].rights;
379             a += 1;
380             break;
381
382         case 1:
383             c += 1;
384             break;
385
386         default:
387             printf("CmpInt() returned bogus value. Aborting ...\n");
388             abort();
389         }
390     negrights = 0;
391     c = 0;
392     a = acl->total - 1;
393     while ((c < groups->prlist_len) && (a > acl->total - acl->negative - 1))
394         switch (CmpInt(acl->entries[a].id, groups->prlist_val[c])) {
395         case -1:
396             a -= 1;
397             break;
398         case 0:
399             negrights |= acl->entries[a].rights;
400             a -= 1;
401             break;
402         case 1:
403             c += 1;
404             break;
405         }
406     *rights = temprights & (~negrights);
407     return (0);
408 }
409
410 int
411 acl_Initialize(version)
412      char *version;
413 {
414     /* I'm sure we need to do some initialization, I'm just not quite sure what yet! */
415     if (strcmp(version, ACL_VERSION) != 0) {
416         fprintf(stderr, "Wrong version of acl package!\n");
417         fprintf(stderr, "This is version %s, file server passed in %s.\n",
418                 ACL_VERSION, version);
419     }
420 #ifdef AFS_PTHREAD_ENV
421     assert(pthread_mutex_init(&acl_list_mutex, NULL) == 0);
422 #endif /* AFS_PTHREAD_ENV */
423     return 0;
424 }
425
426 int
427 acl_IsAMember(aid, cps)
428      afs_int32 aid;
429      prlist *cps;
430 {
431     afs_int32 i;
432
433     for (i = 0; i < cps->prlist_len; i++)
434         if (cps->prlist_val[i] == aid)
435             return 1;
436     return 0;
437 }
438
439
440 static int
441 AddToList(pflist, elem)
442      struct freeListEntry **pflist;
443      struct freeListEntry *elem;
444 {
445     /* Adds elem to the freelist flist;  returns 0 */
446 #ifdef AFS_PTHREAD_ENV
447     assert(pthread_mutex_lock(&acl_list_mutex) == 0);
448 #endif /* AFS_PTHREAD_ENV */
449     elem->next = *pflist;
450     *pflist = elem;
451 #ifdef AFS_PTHREAD_ENV
452     assert(pthread_mutex_unlock(&acl_list_mutex) == 0);
453 #endif /* AFS_PTHREAD_ENV */
454     return 0;
455 }
456
457 static int
458 GetFromList(pflist, elem, minsize)
459      struct freeListEntry **pflist;
460      struct freeListEntry **elem;
461      afs_int32 minsize;
462 {
463     /* Looks for an element whose body is at least minsize bytes in the freelist flist.  If found, unlinks it, puts its address in elem, and returns 0, else returns -1.  A trivial first-fit algorithm is used. */
464
465     struct freeListEntry *y, *z;
466
467 #ifdef AFS_PTHREAD_ENV
468     assert(pthread_mutex_lock(&acl_list_mutex) == 0);
469 #endif /* AFS_PTHREAD_ENV */
470     if (*pflist == NULL) {
471 #ifdef AFS_PTHREAD_ENV
472         assert(pthread_mutex_unlock(&acl_list_mutex) == 0);
473 #endif /* AFS_PTHREAD_ENV */
474         return -1;
475     }
476     for (y = *pflist, z = NULL; y != NULL; z = y, y = y->next) {
477         if (y->size >= minsize) {
478             *elem = y;
479             if (z == NULL) {    /* pulling off the head */
480                 *pflist = y->next;
481 #ifdef AFS_PTHREAD_ENV
482                 assert(pthread_mutex_unlock(&acl_list_mutex) == 0);
483 #endif /* AFS_PTHREAD_ENV */
484                 return 0;
485             }
486             z->next = y->next;
487 #ifdef AFS_PTHREAD_ENV
488             assert(pthread_mutex_unlock(&acl_list_mutex) == 0);
489 #endif /* AFS_PTHREAD_ENV */
490             return 0;
491         }
492     }
493 #ifdef AFS_PTHREAD_ENV
494     assert(pthread_mutex_unlock(&acl_list_mutex) == 0);
495 #endif /* AFS_PTHREAD_ENV */
496     return -1;
497 }