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