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