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