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