Import Upstream version 1.8.5
[hcoop/debian/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 = 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 = 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 = 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 = 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 = 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 *input;
671 char tmp_str[AFS_PIOCTL_MAXSIZE];
672
673 if (uss_verbose)
674 fprintf(stderr,
675 "Setting disk quota on volume mounted at '%s' to %d blocks\n",
676 a_path, a_q);
677
678 status = (uss_VolumeStatus_t *) tmp_str;
679 status->MinQuota = status->MaxQuota = -1;
680 status->MaxQuota = a_q;
681
682 input = (char *)status + sizeof(*status);
683 *(input++) = '\0';
684 *(input++) = '\0';
685 *(input++) = '\0';
686
687 code = uss_fs_SetVolStat(a_path, tmp_str, sizeof(*status) + 3);
688 if (code) {
689 afs_com_err(uss_whoami, code, "while setting disk quota");
690 #ifdef USS_ACL_DB
691 printf("%s: uss_fs_SetVolStat() error code: %d, errno is %d\n", rn,
692 code, errno);
693 #endif /* USS_ACL_DB */
694 return (code);
695 }
696
697 return (0);
698
699 } /*uss_acl_SetDiskQuota */
700
701
702 /*-----------------------------------------------------------------------
703 * EXPORTED uss_acl_CleanUp
704 *
705 * Environment:
706 * The uss_currentDir variable contains directories touched by the
707 * given operation.
708 *
709 * Side Effects:
710 * As advertised.
711 *------------------------------------------------------------------------*/
712
713 afs_int32
714 uss_acl_CleanUp(void)
715 { /*uss_acl_CleanUp */
716
717 struct uss_subdir *t, *old_t = NULL;
718 char tmp_str[uss_MAX_SIZE];
719
720 /*
721 * Restore the ACL for each directory created for our caller to its
722 * original setting. The uss_AccountCreator's access was (potentially)
723 * amplified to full rights - any excess ability must now be removed.
724 */
725 for (t = uss_currentDir; t != NULL; t = t->previous) {
726 sprintf(tmp_str, "%s %s", t->path, t->finalACL);
727 if (uss_verbose)
728 fprintf(stderr, "Setting ACL: '%s'\n", tmp_str);
729 if (!uss_DryRun)
730 uss_acl_SetAccess(tmp_str, 1, 0);
731 else
732 fprintf(stderr, "\t[Dry run: uss_acl_SetAccess(%s) on '%s']\n",
733 tmp_str, t->path);
734 free(t->path);
735 if (old_t)
736 free(old_t);
737 old_t = t;
738 } /*Remove caller from user directory ACL */
739
740 if (old_t)
741 free(old_t);
742
743 /*
744 * Return successfully.
745 */
746 return (0);
747
748 } /*uss_acl_CleanUp */