/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* * Implementation of the ACL and quota-related operations used * by the AFS user account facility. */ /* * --------------------- Required definitions --------------------- */ #include #include #include #undef VIRTUE #undef VICE #include #include #include #include #include #include "uss_acl.h" #include "uss_common.h" #include "uss_fs.h" #define MAXNAME 100 #undef USS_ACL_DB struct Acl { int nplus; int nminus; struct AclEntry *pluslist; struct AclEntry *minuslist; }; struct AclEntry { struct AclEntry *next; char name[MAXNAME]; afs_int32 rights; }; static int PruneList(struct AclEntry **a_aclPP); /*------------------------------------------------------------------------ * static foldcmp * * Description: * String comparison with case-folding. * * Arguments: * a_str1 : First string. * a_str2 : Second string. * * Returns: * 0 if the two strings match, * 1 otherwise. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static int foldcmp(char *a_str1, char *a_str2) { /*foldcmp */ char t, u; while (1) { t = *a_str1++; u = *a_str2++; if (t >= 'A' && t <= 'Z') t += 0x20; if (u >= 'A' && u <= 'Z') u += 0x20; if (t != u) return (1); /*Difference */ if (t == 0) return (0); /*Match */ } } /*foldcmp */ /*------------------------------------------------------------------------ * static Convert * * Description: * Convert rights as expressed in a character string to an integer * with the appropriate bits set. * * Arguments: * a_rights : String version of access rights. * * Returns: * Translated rights. * * Environment: * Nothing interesting. * * Side Effects: * Exits the program if a badly-formatted rights string is * passed in. *------------------------------------------------------------------------*/ static afs_int32 Convert(char *a_rights) { /*Convert */ int i, len; afs_int32 mode; char tc; if (!strcmp(a_rights, "read")) return (PRSFS_READ | PRSFS_LOOKUP); if (!strcmp(a_rights, "write")) return (PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK); if (!strcmp(a_rights, "mail")) return (PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP); if (!strcmp(a_rights, "all")) return (PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE | PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER); if (!strcmp(a_rights, "none")) return (0); len = strlen(a_rights); mode = 0; for (i = 0; i < len; i++) { tc = *a_rights++; if (tc == 'r') mode |= PRSFS_READ; else if (tc == 'l') mode |= PRSFS_LOOKUP; else if (tc == 'i') mode |= PRSFS_INSERT; else if (tc == 'd') mode |= PRSFS_DELETE; else if (tc == 'w') mode |= PRSFS_WRITE; else if (tc == 'k') mode |= PRSFS_LOCK; else if (tc == 'a') mode |= PRSFS_ADMINISTER; else { printf("%s: Bogus rights character '%c'.\n", uss_whoami, tc); exit(1); } } return (mode); } /*Convert */ /*------------------------------------------------------------------------ * static FindList * * Description: * Find the entry with the given name in the passed-in ACL. * * Arguments: * a_alist : Internalized ACL to look through. * a_name : Name to find. * * Returns: * Ptr to the ACL entry found, or null pointer if none. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static struct AclEntry * FindList(struct AclEntry *a_alist, char *a_name) { /*FindList */ while (a_alist) { if (!foldcmp(a_alist->name, a_name)) return (a_alist); a_alist = a_alist->next; } return (0); } /*FindList */ /*------------------------------------------------------------------------ * static ChangeList * * Description: * Set the rights for the named entry to the given value, creating * an entry if one does not yet exist. * * Arguments: * a_al : Ptr to the internalized ACL. * a_plus : Positive or negative rights? * a_name : Name to look for. * a_rights : Rights to set. * * Returns: * 0 if the two strings match, * 1 otherwise. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static void ChangeList(struct Acl *a_al, afs_int32 a_plus, char *a_name, afs_int32 a_rights) { /*ChangeList */ struct AclEntry *tlist; tlist = (a_plus ? a_al->pluslist : a_al->minuslist); tlist = FindList(tlist, a_name); if (tlist) { /* * Found the item already in the list. */ tlist->rights = a_rights; if (a_plus) a_al->nplus -= PruneList(&a_al->pluslist); else a_al->nminus -= PruneList(&a_al->minuslist); return; } /* * Otherwise, we make a new item and plug in the new data. */ tlist = (struct AclEntry *)malloc(sizeof(struct AclEntry)); strcpy(tlist->name, a_name); tlist->rights = a_rights; if (a_plus) { tlist->next = a_al->pluslist; a_al->pluslist = tlist; a_al->nplus++; if (a_rights == 0) a_al->nplus -= PruneList(&a_al->pluslist); } else { tlist->next = a_al->minuslist; a_al->minuslist = tlist; a_al->nminus++; if (a_rights == 0) a_al->nminus -= PruneList(&a_al->minuslist); } } /*ChangeList */ /*------------------------------------------------------------------------ * static PruneList * * Description: * Remove all entries whose rights fields are set to zero. * * Arguments: * a_aclPP : Ptr to the location of the ACL to purne. * * Returns: * Number of entries pruned off. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static int PruneList(struct AclEntry **a_aclPP) { /*PruneList */ struct AclEntry **lPP; struct AclEntry *tP, *nP; afs_int32 ctr; ctr = 0; lPP = a_aclPP; for (tP = *a_aclPP; tP; tP = nP) { if (tP->rights == 0) { *lPP = tP->next; nP = tP->next; free(tP); ctr++; } else { nP = tP->next; lPP = &tP->next; } } return (ctr); } /*PruneList */ /*------------------------------------------------------------------------ * static SkipLine * * Description: * Skip chars until we eat a newline. * * Arguments: * a_str : String to process. * * Returns: * Ptr to the first char past the newline. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static char * SkipLine(char *a_str) { /*SkipLine */ while (*a_str != '\n') a_str++; a_str++; return (a_str); } /*SkipLine */ /*------------------------------------------------------------------------ * static EmptyAcl * * Description: * Create an empty internalized ACL. * * Arguments: * None. * * Returns: * Ptr to an initialized and empty internalized ACL. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static struct Acl * EmptyAcl(void) { /*EmptyAcl */ struct Acl *tp; tp = (struct Acl *)malloc(sizeof(struct Acl)); tp->nplus = tp->nminus = 0; tp->pluslist = tp->minuslist = 0; return (tp); } /*EmptyAcl */ /*------------------------------------------------------------------------ * static ParseAcl * * Description: * Given an externalized ACL (i.e., in string format), convert it * into its internalized form. * * Arguments: * a_str : Ptr to Externalized ACL. * * Returns: * Ptr to the equivalent internalized ACL. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static struct Acl * ParseAcl(char *a_str) { /*ParseAcl */ int nplus, nminus, i, trights; char tname[MAXNAME]; struct AclEntry *first, *last, *tl; struct Acl *ta; /* * Pull out the number of positive & negative entries in the externalized * ACL. */ sscanf(a_str, "%d", &nplus); a_str = SkipLine(a_str); sscanf(a_str, "%d", &nminus); a_str = SkipLine(a_str); /* * Allocate and initialize the first entry. */ ta = (struct Acl *)malloc(sizeof(struct Acl)); ta->nplus = nplus; ta->nminus = nminus; /* * Translate the positive entries. */ last = 0; first = 0; for (i = 0; i < nplus; i++) { sscanf(a_str, "%100s %d", tname, &trights); a_str = SkipLine(a_str); tl = (struct AclEntry *)malloc(sizeof(struct AclEntry)); if (!first) first = tl; strcpy(tl->name, tname); tl->rights = trights; tl->next = 0; if (last) last->next = tl; last = tl; } ta->pluslist = first; /* * Translate the negative entries. */ last = 0; first = 0; for (i = 0; i < nminus; i++) { sscanf(a_str, "%100s %d", tname, &trights); a_str = SkipLine(a_str); tl = (struct AclEntry *)malloc(sizeof(struct AclEntry)); if (!first) first = tl; strcpy(tl->name, tname); tl->rights = trights; tl->next = 0; if (last) last->next = tl; last = tl; } ta->minuslist = first; return (ta); } /*ParseAcl */ /*------------------------------------------------------------------------ * static AclToString * * Description: * Given an internalized ACL, convert it to its externalized form, * namely a character string. * * Arguments: * a_acl : Internalized ACL to externalize. * * Returns: * Ptr to the externalized version. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static char * AclToString(struct Acl *a_acl) { /*AclToString */ static char mydata[AFS_PIOCTL_MAXSIZE]; char tstring[AFS_PIOCTL_MAXSIZE]; struct AclEntry *tp; /* * Print out the number of positive & negative entries on one line. */ sprintf(mydata, "%d\n%d\n", a_acl->nplus, a_acl->nminus); /* * Externalize the positive list. */ for (tp = a_acl->pluslist; tp; tp = tp->next) { sprintf(tstring, "%s %d\n", tp->name, tp->rights); strcat(mydata, tstring); } /* * Externalize the negative list. */ for (tp = a_acl->minuslist; tp; tp = tp->next) { sprintf(tstring, "%s %d\n", tp->name, tp->rights); strcat(mydata, tstring); } return (mydata); } /*AclToString */ /*------------------------------------------------------------------------ * static uss_acl_SetAccess * * Environment: * ACL comes in as a single string, prefixed with the pathname to * which the ACL is applied. The a_clear argument, if set, causes * us to act like sac instead of sa. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_acl_SetAccess(char *a_access, int a_clear, int a_negative) { /*uss_acl_SetAccess */ afs_int32 code; #ifdef USS_ACL_DB static char rn[] = "uss_acl_SetAccess"; #endif struct Acl *ta; char *externalizedACL; int plusp; afs_int32 rights; char tmp_str[AFS_PIOCTL_MAXSIZE]; char path_field[AFS_PIOCTL_MAXSIZE], user_field[64], rights_field[64], *tp; int overflow; plusp = !a_negative; /* * Pull out the pathname from our argument. */ tp = uss_common_FieldCp(path_field, a_access, ' ', sizeof(path_field), &overflow); if (overflow) { fprintf(stderr, "%s: * Pathname field too long (max is %" AFS_SIZET_FMT " chars)\n", uss_whoami, sizeof(path_field)); return (-1); } /* * Ask the Cache Manager to give us the externalized ACL for the * given directory. */ code = uss_fs_GetACL(path_field, tmp_str, AFS_PIOCTL_MAXSIZE); if (code) { afs_com_err(uss_whoami, code, "while getting access list for %s", path_field); #ifdef USS_ACL_DB printf("%s: Error code from uss_fs_GetACL %d, errno %d\n", rn, code, errno); #endif /* USS_ACL_DB */ return (code); } #ifdef USS_ACL_DB else printf("%s: ACL for pathname '%s' is: '%s'\n", rn, path_field, tmp_str); #endif /* USS_ACL_DB */ /* * Generate the proper internalized ACL. */ if (a_clear) ta = EmptyAcl(); else ta = ParseAcl(tmp_str); /* * For each given entry, pull out the information and alter the * internalized ACL to reflect it. */ while (*tp != '\0') { tp = uss_common_FieldCp(user_field, tp, ' ', 64, &overflow); if (overflow) { fprintf(stderr, "%s: * User field too long (max is 64 chars)\n", uss_whoami); return (-1); } if (*tp == '\0') { printf("%s: Missing second half of user/access pair.\n", uss_whoami); return (1); } tp = uss_common_FieldCp(rights_field, tp, ' ', 64, &overflow); if (overflow) { fprintf(stderr, "%s: * Rights field too long (max is 64 chars)\n", uss_whoami); return (-1); } rights = Convert(rights_field); ChangeList(ta, plusp, user_field, rights); } /* * Externalize the fully-processed internal ACL, then pass it back * to the Cache Manager. */ externalizedACL = AclToString(ta); code = uss_fs_SetACL(path_field, externalizedACL, strlen(externalizedACL) + 1); if (code) { #ifdef USS_ACL_DB printf("%s: uss_fs_SetACL() failed, code is %d, errno is %d\n", rn, code, errno); #endif /* USS_ACL_DB */ if (errno == EINVAL) { printf("Can't set ACL for directory '%s' to '%s'\n", path_field, externalizedACL); printf("Invalid argument, possible reasons include:\n"); printf("\t1. File not in AFS, or\n"); printf("\t2. Too many users on the ACL, or\n"); printf("\t3. Non-existent user or group on ACL.\n"); return (code); } else { afs_com_err(uss_whoami, code, "while setting the access list"); return (code); } } return (0); } /*uss_acl_SetAccess */ /*------------------------------------------------------------------------ * static us_acl_SetDiskQuota * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_acl_SetDiskQuota(char *a_path, int a_q) { /*uss_acl_SetDiskQuota */ afs_int32 code; #ifdef USS_ACL_DB static char rn[] = "uss_acl_SetDiskQuota"; #endif uss_VolumeStatus_t *status; char *name, *motd, *offmsg; char *input; char tmp_str[AFS_PIOCTL_MAXSIZE]; if (uss_verbose) fprintf(stderr, "Setting disk quota on volume mounted at '%s' to %d blocks\n", a_path, a_q); status = (uss_VolumeStatus_t *) tmp_str; status->MinQuota = status->MaxQuota = -1; name = motd = offmsg = NULL; status->MaxQuota = a_q; input = (char *)status + sizeof(*status); *(input++) = '\0'; *(input++) = '\0'; *(input++) = '\0'; code = uss_fs_SetVolStat(a_path, tmp_str, sizeof(*status) + 3); if (code) { afs_com_err(uss_whoami, code, "while setting disk quota"); #ifdef USS_ACL_DB printf("%s: uss_fs_SetVolStat() error code: %d, errno is %d\n", rn, code, errno); #endif /* USS_ACL_DB */ return (code); } return (0); } /*uss_acl_SetDiskQuota */ /*----------------------------------------------------------------------- * EXPORTED uss_acl_CleanUp * * Environment: * The uss_currentDir variable contains directories touched by the * given operation. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_acl_CleanUp(void) { /*uss_acl_CleanUp */ struct uss_subdir *t, *old_t = NULL; char tmp_str[uss_MAX_SIZE]; /* * Restore the ACL for each directory created for our caller to its * original setting. The uss_AccountCreator's access was (potentially) * amplified to full rights - any excess ability must now be removed. */ for (t = uss_currentDir; t != NULL; t = t->previous) { sprintf(tmp_str, "%s %s", t->path, t->finalACL); if (uss_verbose) fprintf(stderr, "Setting ACL: '%s'\n", tmp_str); if (!uss_DryRun) uss_acl_SetAccess(tmp_str, 1, 0); else fprintf(stderr, "\t[Dry run: uss_acl_SetAccess(%s) on '%s']\n", tmp_str, t->path); free(t->path); if (old_t) free(old_t); old_t = t; } /*Remove caller from user directory ACL */ if (old_t) free(old_t); /* * Return successfully. */ return (0); } /*uss_acl_CleanUp */