pull-prototypes-to-head-20020821
[openafs.git] / src / uss / uss_acl.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  *      Implementation of the ACL and quota-related operations used
12  *      by the AFS user account facility.
13  */
14
15 /*
16  * --------------------- Required definitions ---------------------
17  */
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 RCSID("$Header$");
22
23 #include "uss_acl.h"
24 #include "uss_common.h"
25 #include "uss_fs.h"
26 #include <rx/xdr.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <netdb.h>
30 #include <errno.h>
31
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #else
35 #ifdef HAVE_STRINGS_H
36 #include <strings.h>
37 #endif
38 #endif
39
40 #undef VIRTUE
41 #undef VICE
42 #include <afs/afsint.h>
43 #include <afs/prs_fs.h>
44 #include <afs/com_err.h>
45
46 #define MAXNAME 100
47 #define MAXSIZE 2048
48
49 #undef USS_ACL_DB
50
51 struct Acl {
52     int nplus;
53     int nminus;
54     struct AclEntry *pluslist;
55     struct AclEntry *minuslist;
56 };
57
58 struct AclEntry {
59     struct AclEntry *next;
60     char name[MAXNAME];
61     afs_int32 rights;
62 };
63
64 static int PruneList();
65
66
67 /*------------------------------------------------------------------------
68  * static foldcmp
69  *
70  * Description:
71  *      String comparison with case-folding.
72  *
73  * Arguments:
74  *      a_str1 : First string.
75  *      a_str2 : Second string.
76  *
77  * Returns:
78  *      0 if the two strings match,
79  *      1 otherwise.
80  *
81  * Environment:
82  *      Nothing interesting.
83  *
84  * Side Effects:
85  *      As advertised.
86  *------------------------------------------------------------------------*/
87
88 static foldcmp(a_str1, a_str2)
89     register char *a_str1;
90     register char *a_str2;
91
92 { /*foldcmp*/
93
94     register char t, u;
95
96     while (1) {
97         t = *a_str1++;
98         u = *a_str2++;
99         if (t >= 'A' && t <= 'Z') t += 0x20;
100         if (u >= 'A' && u <= 'Z') u += 0x20;
101         if (t != u)
102             return(1);  /*Difference*/
103         if (t == 0)
104             return(0);  /*Match*/
105     }
106 } /*foldcmp*/
107
108
109 /*------------------------------------------------------------------------
110  * static Convert
111  *
112  * Description:
113  *      Convert rights as expressed in a character string to an integer
114  *      with the appropriate bits set.
115  *
116  * Arguments:
117  *      a_rights : String version of access rights.
118  *
119  * Returns:
120  *      Translated rights.
121  *
122  * Environment:
123  *      Nothing interesting.
124  *
125  * Side Effects:
126  *      Exits the program if a badly-formatted rights string is
127  *      passed in.
128  *------------------------------------------------------------------------*/
129
130 static afs_int32 Convert(a_rights)
131     register char *a_rights;
132
133 { /*Convert*/
134
135     register int i, len;
136     afs_int32 mode;
137     register char tc;
138
139     if (!strcmp(a_rights,"read"))
140         return(PRSFS_READ | PRSFS_LOOKUP);
141
142     if (!strcmp(a_rights, "write"))
143         return(PRSFS_READ
144                | PRSFS_LOOKUP
145                | PRSFS_INSERT
146                | PRSFS_DELETE
147                | PRSFS_WRITE
148                | PRSFS_LOCK);
149
150     if (!strcmp(a_rights, "mail"))
151         return(PRSFS_INSERT
152                | PRSFS_LOCK
153                | PRSFS_LOOKUP);
154
155     if (!strcmp(a_rights, "all"))
156         return(PRSFS_READ
157                | PRSFS_LOOKUP
158                | PRSFS_INSERT
159                | PRSFS_DELETE
160                | PRSFS_WRITE
161                | PRSFS_LOCK
162                | PRSFS_ADMINISTER);
163
164     if (!strcmp(a_rights, "none"))
165         return(0);
166
167     len = strlen(a_rights);
168     mode = 0;
169     for (i = 0; i < len; i++) {
170         tc = *a_rights++;
171         if (tc == 'r')      mode |= PRSFS_READ;
172         else if (tc == 'l') mode |= PRSFS_LOOKUP;
173         else if (tc == 'i') mode |= PRSFS_INSERT;
174         else if (tc == 'd') mode |= PRSFS_DELETE;
175         else if (tc == 'w') mode |= PRSFS_WRITE;
176         else if (tc == 'k') mode |= PRSFS_LOCK;
177         else if (tc == 'a') mode |= PRSFS_ADMINISTER;
178         else {
179             printf("%s: Bogus rights character '%c'.\n",
180                    uss_whoami, tc);
181             exit(1);
182         }
183     }
184     return(mode);
185
186 } /*Convert*/
187
188
189 /*------------------------------------------------------------------------
190  * static FindList
191  *
192  * Description:
193  *      Find the entry with the given name in the passed-in ACL.
194  *
195  * Arguments:
196  *      a_alist : Internalized ACL to look through.
197  *      a_name  : Name to find.
198  *
199  * Returns:
200  *      Ptr to the ACL entry found, or null pointer if none.
201  *
202  * Environment:
203  *      Nothing interesting.
204  *
205  * Side Effects:
206  *      As advertised.
207  *------------------------------------------------------------------------*/
208
209 static struct AclEntry *FindList(a_alist, a_name)
210     register struct AclEntry *a_alist;
211     char *a_name;
212
213 { /*FindList*/
214
215     while (a_alist) {
216         if (!foldcmp(a_alist->name, a_name))
217             return(a_alist);
218         a_alist = a_alist->next;
219     }
220     return(0);
221
222 } /*FindList*/
223
224
225 /*------------------------------------------------------------------------
226  * static ChangeList
227  *
228  * Description:
229  *      Set the rights for the named entry to the given value, creating
230  *      an entry if one does not yet exist.
231  *
232  * Arguments:
233  *      a_al     : Ptr to the internalized ACL.
234  *      a_plus   : Positive or negative rights?
235  *      a_name   : Name to look for.
236  *      a_rights : Rights to set.
237  *
238  * Returns:
239  *      0 if the two strings match,
240  *      1 otherwise.
241  *
242  * Environment:
243  *      Nothing interesting.
244  *
245  * Side Effects:
246  *      As advertised.
247  *------------------------------------------------------------------------*/
248
249 static void ChangeList (a_al, a_plus, a_name, a_rights)
250     struct Acl *a_al;
251     afs_int32 a_plus;
252     char *a_name;
253     afs_int32 a_rights;
254
255 { /*ChangeList*/
256
257     struct AclEntry *tlist;
258
259     tlist = (a_plus ? a_al->pluslist : a_al->minuslist);
260     tlist = FindList(tlist, a_name);
261     if (tlist) {
262         /*
263          * Found the item already in the list.
264          */
265         tlist->rights = a_rights;
266         if (a_plus)
267             a_al->nplus -= PruneList(&a_al->pluslist);
268         else
269             a_al->nminus -= PruneList(&a_al->minuslist);
270         return;
271     }
272
273     /*
274      * Otherwise, we make a new item and plug in the new data.
275      */
276     tlist = (struct AclEntry *) malloc(sizeof(struct AclEntry));
277     strcpy(tlist->name, a_name);
278     tlist->rights = a_rights;
279     if (a_plus) {
280         tlist->next = a_al->pluslist;
281         a_al->pluslist = tlist;
282         a_al->nplus++;
283         if (a_rights == 0)
284             a_al->nplus -= PruneList(&a_al->pluslist);
285     }
286     else {
287         tlist->next = a_al->minuslist;
288         a_al->minuslist = tlist;
289         a_al->nminus++;
290         if (a_rights == 0)
291             a_al->nminus -= PruneList(&a_al->minuslist);
292     }
293 } /*ChangeList*/
294
295
296 /*------------------------------------------------------------------------
297  * static PruneList
298  *
299  * Description:
300  *      Remove all entries whose rights fields are set to zero.
301  *
302  * Arguments:
303  *      a_aclPP : Ptr to the location of the ACL to purne.
304  *
305  * Returns:
306  *      Number of entries pruned off.
307  *
308  * Environment:
309  *      Nothing interesting.
310  *
311  * Side Effects:
312  *      As advertised.
313  *------------------------------------------------------------------------*/
314
315 static int PruneList (a_aclPP)
316     struct AclEntry **a_aclPP;
317
318 { /*PruneList*/
319
320     struct AclEntry **lPP;
321     struct AclEntry *tP, *nP;
322     afs_int32 ctr;
323
324     ctr = 0;
325     lPP = a_aclPP;
326     for (tP = *a_aclPP; tP; tP = nP) {
327         if (tP->rights == 0) {
328             *lPP = tP->next;
329             nP = tP->next;
330             free(tP);
331             ctr++;
332         }
333         else {
334             nP = tP->next;
335             lPP = &tP->next;
336         }
337     }
338
339     return(ctr);
340
341 } /*PruneList*/
342
343
344 /*------------------------------------------------------------------------
345  * static SkipLine
346  *
347  * Description:
348  *      Skip chars until we eat a newline.
349  *
350  * Arguments:
351  *      a_str : String to process.
352  *
353  * Returns:
354  *      Ptr to the first char past the newline.
355  *
356  * Environment:
357  *      Nothing interesting.
358  *
359  * Side Effects:
360  *      As advertised.
361  *------------------------------------------------------------------------*/
362
363 static char *SkipLine(a_str)
364     register char *a_str;
365
366 { /*SkipLine*/
367
368     while (*a_str !='\n') a_str++;
369     a_str++;
370     return(a_str);
371
372 } /*SkipLine*/
373
374
375 /*------------------------------------------------------------------------
376  * static EmptyAcl
377  *
378  * Description:
379  *      Create an empty internalized ACL.
380  *
381  * Arguments:
382  *      None.
383  *
384  * Returns:
385  *      Ptr to an initialized and empty internalized ACL.
386  *
387  * Environment:
388  *      Nothing interesting.
389  *
390  * Side Effects:
391  *      As advertised.
392  *------------------------------------------------------------------------*/
393
394 static struct Acl *EmptyAcl()
395
396 { /*EmptyAcl*/
397
398     register struct Acl *tp;
399
400     tp = (struct Acl *) malloc(sizeof (struct Acl));
401     tp->nplus = tp->nminus = 0;
402     tp->pluslist = tp->minuslist = 0;
403     return(tp);
404
405 } /*EmptyAcl*/
406
407
408 /*------------------------------------------------------------------------
409  * static ParseAcl
410  *
411  * Description:
412  *      Given an externalized ACL (i.e., in string format), convert it
413  *      into its internalized form.
414  *
415  * Arguments:
416  *      a_str : Ptr to Externalized ACL.
417  *
418  * Returns:
419  *      Ptr to the equivalent internalized ACL.
420  *
421  * Environment:
422  *      Nothing interesting.
423  *
424  * Side Effects:
425  *      As advertised.
426  *------------------------------------------------------------------------*/
427
428 static struct Acl *ParseAcl(a_str)
429     char *a_str;
430
431 { /*ParseAcl*/
432
433     int nplus, nminus, i, trights;
434     char tname[MAXNAME];
435     struct AclEntry *first, *last, *tl;
436     struct Acl *ta;
437
438     /*
439      * Pull out the number of positive & negative entries in the externalized
440      * ACL.
441      */
442     sscanf(a_str, "%d", &nplus);
443     a_str = SkipLine(a_str);
444     sscanf(a_str, "%d", &nminus);
445     a_str = SkipLine(a_str);
446
447     /*
448      * Allocate and initialize the first entry.
449      */
450     ta = (struct Acl *) malloc(sizeof (struct Acl));
451     ta->nplus = nplus;
452     ta->nminus = nminus;
453
454     /*
455      * Translate the positive entries.
456      */
457     last = 0;
458     first = 0;
459     for(i = 0; i < nplus; i++) {
460         sscanf(a_str, "%100s %d", tname, &trights);
461         a_str = SkipLine(a_str);
462         tl = (struct AclEntry *) malloc(sizeof(struct AclEntry));
463         if (!first)
464             first = tl;
465         strcpy(tl->name, tname);
466         tl->rights = trights;
467         tl->next = 0;
468         if (last)
469             last->next = tl;
470         last = tl;
471     }
472     ta->pluslist = first;
473
474     /*
475      * Translate the negative entries.
476      */
477     last = 0;
478     first = 0;
479     for (i = 0; i < nminus; i++) {
480         sscanf(a_str, "%100s %d", tname, &trights);
481         a_str = SkipLine(a_str);
482         tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
483         if (!first)
484             first = tl;
485         strcpy(tl->name, tname);
486         tl->rights = trights;
487         tl->next = 0;
488         if (last)
489             last->next = tl;
490         last = tl;
491     }
492     ta->minuslist = first;
493
494     return(ta);
495
496 } /*ParseAcl*/
497
498
499 /*------------------------------------------------------------------------
500  * static AclToString
501  *
502  * Description:
503  *      Given an internalized ACL, convert it to its externalized form,
504  *      namely a character string.
505  *
506  * Arguments:
507  *      a_acl : Internalized ACL to externalize.
508  *
509  * Returns:
510  *      Ptr to the externalized version.
511  *
512  * Environment:
513  *      Nothing interesting.
514  *
515  * Side Effects:
516  *      As advertised.
517  *------------------------------------------------------------------------*/
518
519 static char *AclToString(a_acl)
520     struct Acl *a_acl;
521
522 { /*AclToString*/
523
524     static char mydata[MAXSIZE];
525     char tstring[MAXSIZE];
526     struct AclEntry *tp;
527
528     /*
529      * Print out the number of positive & negative entries on one line.
530      */
531     sprintf(mydata, "%d\n%d\n", a_acl->nplus, a_acl->nminus);
532
533     /*
534      * Externalize the positive list.
535      */
536     for (tp = a_acl->pluslist; tp; tp = tp->next) {
537         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
538         strcat(mydata, tstring);
539     }
540
541     /*
542      * Externalize the negative list.
543      */
544     for (tp = a_acl->minuslist; tp; tp = tp->next) {
545         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
546         strcat(mydata, tstring);
547     }
548     return(mydata);
549
550 }  /*AclToString*/
551
552
553 /*------------------------------------------------------------------------
554  * static uss_acl_SetAccess
555  *
556  * Environment:
557  *      ACL comes in as a single string, prefixed with the pathname to
558  *      which the ACL is applied.  The a_clear argument, if set, causes
559  *      us to act like sac instead of sa.
560  *
561  * Side Effects:
562  *      As advertised.
563  *------------------------------------------------------------------------*/
564
565 afs_int32 uss_acl_SetAccess(a_access, a_clear, a_negative)
566     char *a_access;
567     int a_clear;
568     int a_negative;
569
570 { /*uss_acl_SetAccess*/
571
572     register afs_int32 code;
573     static char rn[] = "uss_acl_SetAccess";
574     struct Acl *ta;
575     char *externalizedACL;
576     int plusp;
577     afs_int32 rights;
578     char tmp_str[MAXSIZE];
579     char path_field[MAXSIZE], user_field[64], rights_field[64], *tp;
580     int overflow;
581
582     plusp = !a_negative;
583
584     /*
585      * Pull out the pathname from our argument.
586      */
587     tp = uss_common_FieldCp(path_field, a_access, ' ', sizeof(path_field),
588                             &overflow);
589     if (overflow) {
590         fprintf(stderr,
591                 "%s: * Pathname field too long (max is %d chars)\n",
592                 uss_whoami, sizeof(path_field));
593         return(-1);
594     }
595
596     /*
597      * Ask the Cache Manager to give us the externalized ACL for the
598      * given directory.
599      */
600     code = uss_fs_GetACL(path_field, tmp_str, MAXSIZE);
601     if (code) {
602         com_err(uss_whoami, code,
603                 "while getting access list for %s", path_field);
604 #ifdef USS_ACL_DB
605         printf("%s: Error code from uss_fs_GetACL %d, errno %d\n",
606                rn, code, errno);
607 #endif /* USS_ACL_DB */
608         return(code);
609     }
610 #ifdef USS_ACL_DB
611     else
612         printf("%s: ACL for pathname '%s' is: '%s'\n",
613                rn, path_field, tmp_str);
614 #endif /* USS_ACL_DB */
615
616     /*
617      * Generate the proper internalized ACL.
618      */
619     if (a_clear)
620         ta = EmptyAcl();
621     else
622         ta = ParseAcl(tmp_str);
623
624     /*
625      * For each given entry, pull out the information and alter the
626      * internalized ACL to reflect it.
627      */
628     while(*tp != '\0') {
629         tp = uss_common_FieldCp(user_field, tp, ' ', 64, &overflow);
630         if (overflow) {
631             fprintf(stderr,
632                     "%s: * User field too long (max is 64 chars)\n",
633                     uss_whoami);
634             return(-1);
635         }
636         if (*tp == '\0') {
637             printf("%s: Missing second half of user/access pair.\n",
638                    uss_whoami);
639             return(1);
640         }
641         tp = uss_common_FieldCp(rights_field, tp, ' ', 64, &overflow);
642         if (overflow) {
643             fprintf(stderr,
644                     "%s: * Rights field too long (max is 64 chars)\n",
645                     uss_whoami);
646             return(-1);
647         }
648         rights = Convert(rights_field);
649         ChangeList(ta, plusp, user_field, rights);
650     }
651
652     /*
653      * Externalize the fully-processed internal ACL, then pass it back
654      * to the Cache Manager.
655      */
656     externalizedACL = AclToString(ta);
657     code = uss_fs_SetACL(path_field, externalizedACL,
658                          strlen(externalizedACL)+1);
659     if (code) {
660 #ifdef USS_ACL_DB
661         printf("%s: uss_fs_SetACL() failed, code is %d, errno is %d\n",
662                rn, code, errno);
663 #endif /* USS_ACL_DB */
664         if (errno == EINVAL) {
665             printf("Can't set ACL for directory '%s' to '%s'\n",
666                    path_field, externalizedACL);
667             printf("Invalid argument, possible reasons include:\n");
668             printf("\t1. File not in AFS, or\n");
669             printf("\t2. Too many users on the ACL, or\n");
670             printf("\t3. Non-existent user or group on ACL.\n");
671             return(code);
672         }
673         else {
674             com_err(uss_whoami, code, "while setting the access list");
675             return(code);
676         }
677     }
678     return(0);
679
680 } /*uss_acl_SetAccess*/
681
682
683 /*------------------------------------------------------------------------
684  * static us_acl_SetDiskQuota
685  *
686  * Environment:
687  *      Nothing interesting.
688  *
689  * Side Effects:
690  *      As advertised.
691  *------------------------------------------------------------------------*/
692
693 afs_int32 uss_acl_SetDiskQuota(a_path, a_q)
694     char *a_path;
695     int a_q;
696
697 { /*uss_acl_SetDiskQuota*/
698
699     register afs_int32 code;
700     static char rn[] = "uss_acl_SetDiskQuota";
701     uss_VolumeStatus_t *status;
702     char *name, *motd, *offmsg;
703     char *input;
704     char tmp_str[MAXSIZE];
705
706     if (uss_verbose)
707         fprintf(stderr,
708                 "Setting disk quota on volume mounted at '%s' to %d blocks\n",
709                 a_path, a_q);
710
711     status = (uss_VolumeStatus_t *)tmp_str;
712     status->MinQuota = status->MaxQuota = -1;
713     name = motd = offmsg = NULL;
714     status->MaxQuota = a_q;
715
716     input = (char *)status + sizeof(*status);
717     *(input++) = '\0';  
718     *(input++) = '\0';  
719     *(input++) = '\0';  
720
721     code = uss_fs_SetVolStat(a_path, tmp_str, sizeof(*status)+3);    
722     if (code) {
723         com_err(uss_whoami, code, "while setting disk quota");
724 #ifdef USS_ACL_DB
725         printf("%s: uss_fs_SetVolStat() error code: %d, errno is %d\n",
726                rn, code, errno);
727 #endif /* USS_ACL_DB */
728         return(code);
729     }
730
731     return(0);
732
733 } /*uss_acl_SetDiskQuota*/
734
735
736 /*-----------------------------------------------------------------------
737  * EXPORTED uss_acl_CleanUp
738  *
739  * Environment:
740  *      The uss_currentDir variable contains directories touched by the
741  *      given operation.
742  *
743  * Side Effects:
744  *      As advertised.
745  *------------------------------------------------------------------------*/
746
747 afs_int32 uss_acl_CleanUp()
748
749 { /*uss_acl_CleanUp*/
750
751     static char rn[] = "uss_acl_CleanUp";
752     struct uss_subdir *t, *old_t = NULL;
753     char tmp_str[uss_MAX_SIZE];
754
755     /*
756      * Restore the ACL for each directory created for our caller to its
757      * original setting.  The uss_AccountCreator's access was (potentially)
758      * amplified to full rights - any excess ability must now be removed.
759      */
760     for (t = uss_currentDir; t != NULL; t = t->previous) {
761         sprintf(tmp_str, "%s %s", t->path, t->finalACL);
762         if (uss_verbose)
763             fprintf(stderr, "Setting ACL: '%s'\n", tmp_str);
764         if (!uss_DryRun)
765             uss_acl_SetAccess(tmp_str, 1, 0);
766         else
767             fprintf(stderr,
768                     "\t[Dry run: uss_acl_SetAccess(%s) on '%s']\n",
769                     tmp_str, t->path);
770         free(t->path);
771         if (old_t)
772             free(old_t);
773         old_t = t;
774     } /*Remove caller from user directory ACL*/
775     
776     if (old_t)
777         free(old_t);
778
779     /*
780      * Return successfully.
781      */
782     return(0);
783
784 } /*uss_acl_CleanUp*/