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