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