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