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