/* * 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 */ /* * (5) Add functions to process supergroups: * ChangeIDEntry(), RemoveFromSGEntry(), * AddToSGEntry(), GetListSG2(). * (6) Add code to existing functions to process * supergroups. * 2/1/98 jjm Add mdw's changes for bit mapping for supergroups * * 09/26/02 kwc Move depthsg definition here from ptserver.c since * pt_util needs it defined also, and this file is * common between the two programs. */ #include #include #include #include #include #include #include #include #include #include "ptserver.h" #include "pterror.h" #include "ptprototypes.h" /* Foreign cells are represented by the group system:authuser@cell*/ #define AUTHUSER_GROUP "system:authuser" extern int restricted; extern struct ubik_dbase *dbase; extern struct afsconf_dir *prdir; extern int pr_noAuth; static int inRange(struct prentry *cellEntry, afs_int32 aid); static afs_int32 allocNextId(struct ubik_trans *, struct prentry *); static int AddAuthGroup(struct prentry *tentry, prlist *alist, afs_int32 *size); static char *whoami = "ptserver"; int prp_user_default = PRP_USER_DEFAULT; int prp_group_default = PRP_GROUP_DEFAULT; #if defined(SUPERGROUPS) #include "map.h" afs_int32 depthsg = 5; /* Maximum iterations used during IsAMemberOF */ afs_int32 GetListSG2(struct ubik_trans *at, afs_int32 gid, prlist * alist, afs_int32 * sizeP, afs_int32 depth); struct map *sg_flagged; struct map *sg_found; #define NIL_MAP ((struct map *) 0) /* pt_mywrite hooks into the logic that writes ubik records to disk * at a very low level. It invalidates mappings in sg_flagged/sg_found. * By hoooking in at this level, we ensure that a ubik file reload * invalidates our incore cache. * * We assert records, cheaders and everything else are 0 mod 64. * So, we can always see the first 64 bytes of any record written. * The stuff we're interested in (flags, id) are in the first 8 bytes. * so we can always tell if we're writing a group record. */ int (*pt_save_dbase_write)(struct ubik_dbase *, afs_int32, void *, afs_int32, afs_int32); int pt_mywrite(struct ubik_dbase *tdb, afs_int32 fno, void *bp, afs_int32 pos, afs_int32 count) { afs_uint32 headersize = ntohl(cheader.headerSize); if (fno == 0 && pos + count > headersize) { afs_int32 p, l, c, o; char *cp; p = pos - headersize; cp = bp; l = count; if (p < 0) { l += p; p = 0; } while (l > 0) { o = p % ENTRYSIZE; c = ENTRYSIZE - o; if (c > l) c = l; #define xPT(p,x) ((((struct prentry *)(p))->flags & htonl(PRTYPE)) == htonl(x)) #if DEBUG_SG_MAP if (o) fprintf(stderr, "Writing %d bytes of entry @ %#lx(+%d)\n", c, p - o, o); else if (c == ENTRYSIZE) fprintf(stderr, "Writing %d bytes of entry @ %#lx (%d<%s>,%d)\n", c, p, ntohl(((struct prentry *)cp)->flags), xPT(cp,PRUSER) ? "user" : xPT(cp,PRFREE) ? "free" : xPT(cp,PRGRP) ? "group" : xPT(cp,PRCONT) ? "cont" : xPT(cp,PRCELL) ? "cell" : xPT(cp,PRFOREIGN) ? "foreign" : xPT(cp,PRINST) ? "sub/super instance" : "?", ntohl(((struct prentry *)cp)->id)); else if (c >= 8) fprintf(stderr, "Writing first %d bytes of entry @ %#lx (%d<%s>,%d)\n", c, p, ntohl(((struct prentry *)cp)->flags), xPT(cp,PRUSER) ? "user" : xPT(cp,PRFREE) ? "free" : xPT(cp,PRGRP) ? "group" : xPT(cp,PRCONT) ? "cont" : xPT(cp,PRCELL) ? "cell" : xPT(cp,PRFOREIGN) ? "foreign" : xPT(cp,PRINST) ? "sub/super instance" : "?", ntohl(((struct prentry *)cp)->id)); else fprintf(stderr, "Writing %d bytes of entry @ %#lx\n", c, p); #endif if (!o && c >= 8 && xPT(cp,PRGRP)) { #if DEBUG_SG_MAP if (in_map(sg_found, -ntohl(((struct prentry *)cp)->id))) fprintf(stderr, "Unfound: Removing group %d\n", ntohl(((struct prentry *)cp)->id)); if (in_map(sg_flagged, -ntohl(((struct prentry *)cp)->id))) fprintf(stderr, "Unflag: Removing group %d\n", ntohl(((struct prentry *)cp)->id)); #endif sg_found = bic_map(sg_found, add_map(NIL_MAP, -ntohl(((struct prentry *)cp)->id))); sg_flagged = bic_map(sg_flagged, add_map(NIL_MAP, -ntohl(((struct prentry *)cp)->id))); } cp += c; p += c; l -= c; #undef xPT } } return (*pt_save_dbase_write) (tdb, fno, bp, pos, count); } /* * this function attaches pt_mywrite. It's called once, * just after ubik_ServerInit. */ void pt_hook_write(void) { extern struct ubik_dbase *ubik_dbase; if (ubik_dbase->write != pt_mywrite) { pt_save_dbase_write = ubik_dbase->write; ubik_dbase->write = pt_mywrite; } } #endif /* SUPERGROUPS */ /* CorrectUserName - Check to make sure a user name is OK. It must not include * either a colon (or it would look like a group) or a newline (which can * confuse some ptdb code, depending on the format we're reading from). * This is a predicate, so it return one if name is OK and zero if name is * bogus. */ static int CorrectUserName(char *name) { /* We accept foreign names, so we will deal with '@' later */ if (strchr(name, ':') || strchr(name, '\n')) return 0; if (strlen(name) >= PR_MAXNAMELEN) return 0; return 1; } /* CorrectGroupName - Like the above but handles more complicated cases caused * by including the ownership in the name. The interface works by calculating * the correct name based on a given name and owner. This allows easy use by * rename, which then compares the correct name with the requested new name. */ static afs_int32 CorrectGroupName(struct ubik_trans *ut, char aname[PR_MAXNAMELEN], /* name for group */ afs_int32 cid, /* caller id */ afs_int32 oid, /* owner of group */ afs_int32 admin, /* non-zero if admin */ char cname[PR_MAXNAMELEN]) /* correct name for group */ { afs_int32 code; char *prefix; /* ptr to group owner part */ char *suffix; /* ptr to group name part */ char name[PR_MAXNAMELEN]; /* correct name for group */ struct prentry tentry; if (strlen(aname) >= PR_MAXNAMELEN) return PRBADNAM; /* Determine the correct prefix for the name. */ if (oid == SYSADMINID) prefix = "system"; else { afs_int32 loc = FindByID(ut, oid); if (loc == 0) { /* let admin create groups owned by non-existent ids (probably * setting a group to own itself). Check that they look like * groups (with a colon) or otherwise are good user names. */ if (admin) { strcpy(cname, aname); goto done; } return PRNOENT; } code = pr_Read(ut, 0, loc, &tentry, sizeof(tentry)); if (code) return code; if (ntohl(tentry.flags) & PRGRP) { if ((tentry.count == 0) && !admin) return PRGROUPEMPTY; /* terminate prefix at colon if there is one */ if ((prefix = strchr(tentry.name, ':'))) *prefix = 0; } prefix = tentry.name; } /* only sysadmin allow to use 'system:' prefix */ if ((strcmp(prefix, "system") == 0) && !admin) return PRPERM; strcpy(name, aname); /* in case aname & cname are same */ suffix = strchr(name, ':'); /* let e.g. pt_util create groups with "wrong" names (like * an orphan whose parent ID was reused). Check that they look like * groups (with a colon) or otherwise are good user names. */ if (pr_noAuth) { strcpy(cname, aname); goto done; } if (suffix == 0) { /* sysadmin can make groups w/o ':', but they must still look like * legal user names. */ if (!admin) return PRBADNAM; strcpy(cname, name); } else { if (strlen(prefix) + strlen(suffix) >= PR_MAXNAMELEN) return PRBADNAM; strcpy(cname, prefix); strcat(cname, suffix); } done: /* check for legal name with either group rules or user rules */ if ((suffix = strchr(cname, ':'))) { /* check for confusing characters */ if (strchr(cname, '\n') || /* restrict so recreate can work */ strchr(suffix + 1, ':')) /* avoid multiple colons */ return PRBADNAM; } else { if (!CorrectUserName(cname)) return PRBADNAM; } return 0; } int AccessOK(struct ubik_trans *ut, afs_int32 cid, /* caller id */ struct prentry *tentry, /* object being accessed */ int mem, /* check membership in aid, if group */ int any) /* if set return true */ { afs_int32 flags; afs_int32 oid; afs_int32 aid; if (pr_noAuth) return 1; if (cid == SYSADMINID) return 1; /* special case fileserver */ if (restricted && !IsAMemberOf(ut, cid, SYSADMINID)) { if (mem == PRP_ADD_MEM || mem == PRP_REMOVE_MEM) { /* operation is for adding/removing members from a group */ return 0; } if (mem == 0 && any == 0) { /* operation is for modifying an entry (or some administrative * global operations) */ return 0; } } if (tentry) { flags = tentry->flags; oid = tentry->owner; aid = tentry->id; } else { flags = oid = aid = 0; } if (!(flags & PRACCESS)) { /* provide default access */ if (flags & PRGRP) flags |= prp_group_default; else flags |= prp_user_default; } if (flags & any) return 1; if (oid) { if ((cid == oid) || IsAMemberOf(ut, cid, oid)) return 1; } if (aid > 0) { /* checking on a user */ if (aid == cid) return 1; } else if (aid < 0) { /* checking on group */ if ((flags & mem) && IsAMemberOf(ut, cid, aid)) return 1; } /* Allow members of SYSVIEWERID to get membership and status only */ if (((mem == PRP_STATUS_MEM) || (mem == PRP_MEMBER_MEM) || (any == PRP_OWNED_ANY)) && (IsAMemberOf(ut, cid, SYSVIEWERID))) return 1; if (IsAMemberOf(ut, cid, SYSADMINID)) return 1; return 0; /* no access */ } afs_int32 CreateEntry(struct ubik_trans *at, char aname[PR_MAXNAMELEN], afs_int32 *aid, afs_int32 idflag, afs_int32 flag, afs_int32 oid, afs_int32 creator) { /* get and init a new entry */ afs_int32 code; afs_int32 newEntry; afs_int32 admin; struct prentry tentry, tent; char *atsign; memset(&tentry, 0, sizeof(tentry)); admin = pr_noAuth || IsAMemberOf(at, creator, SYSADMINID); if (oid == 0 || oid == ANONYMOUSID) { if (!admin && creator == 0) return PRBADARG; oid = creator; } if (flag & PRGRP) { code = CorrectGroupName(at, aname, creator, oid, admin, tentry.name); if (code) return code; if (strcmp(aname, tentry.name) != 0) return PRBADNAM; } else { /* non-group must not have colon */ if (!CorrectUserName(aname)) return PRBADNAM; strcpy(tentry.name, aname); } if (FindByName(at, aname, &tent)) return PREXIST; newEntry = AllocBlock(at); if (!newEntry) return PRDBFAIL; tentry.createTime = time(0); if (flag & PRGRP) { tentry.flags = PRGRP; tentry.owner = oid; } else if (flag == 0) { tentry.flags = 0; tentry.owner = SYSADMINID; } else { return PRBADARG; } atsign = strchr(aname, '@'); if (!atsign) { /* A normal user or group. Pick an id for it */ if (idflag) tentry.id = *aid; else { code = AllocID(at, flag, &tentry.id); if (code != PRSUCCESS) return code; } } else if (flag & PRGRP) { /* A foreign group. Its format must be AUTHUSER_GROUP@cellname * Then pick an id for the group. */ int badFormat; *atsign = '\0'; badFormat = strcmp(AUTHUSER_GROUP, aname); *atsign = '@'; if (badFormat) return PRBADNAM; if (idflag) tentry.id = *aid; else { code = AllocID(at, flag, &tentry.id); if (code != PRSUCCESS) return code; } } else { /* A foreign user: @. The foreign user is added to * its representing group. It is */ char *cellGroup; afs_int32 pos, n; struct prentry centry; /* To create the user @ the group AUTHUSER_GROUP@ * must exist. */ if (asprintf(&cellGroup, "%s%s", AUTHUSER_GROUP, atsign) < 0) return PRNOMEM; pos = FindByName(at, cellGroup, ¢ry); free(cellGroup); if (!pos) return PRBADNAM; code = pr_Read(at, 0, pos, ¢ry, sizeof(centry)); if (code) return code; /* cellid is the id of the group representing the cell */ tentry.cellid = ntohl(centry.id); if (idflag) { /* Check if id is good */ if (!inRange(¢ry, *aid)) return PRBADARG; tentry.id = *aid; } else { /* Allocate an ID special for this foreign user. It is based * on the representing group's id and nusers count. */ tentry.id = allocNextId(at, ¢ry); if (!tentry.id) return PRNOIDS; } /* The foreign user will be added to the representing foreign * group. The group can hold up to 30 entries. */ if (!(ntohl(centry.flags) & PRQUOTA)) { centry.flags = htonl(ntohl(centry.flags) | PRQUOTA); centry.ngroups = htonl(30); } n = ntohl(centry.ngroups); if ((n <= 0) && !pr_noAuth) return PRNOMORE; centry.ngroups = htonl(n - 1); /* write updated entry for group */ code = pr_Write(at, 0, pos, ¢ry, sizeof(centry)); if (code) return PRDBFAIL; /* Now add the new user entry to the database */ if (creator == 0) tentry.creator = tentry.id; else tentry.creator = creator; *aid = tentry.id; code = pr_WriteEntry(at, 0, newEntry, &tentry); if (code) return PRDBFAIL; code = AddToIDHash(at, *aid, newEntry); if (code != PRSUCCESS) return code; code = AddToNameHash(at, aname, newEntry); if (code != PRSUCCESS) return code; if (inc_header_word(at, foreigncount, 1)) return PRDBFAIL; /* Now add the entry to the authuser group for this cell. * We will reread the entries for the user and the group * instead of modifying them before writing them in the * previous steps. Although not very efficient, much simpler */ pos = FindByID(at, tentry.cellid); if (!pos) return PRBADNAM; code = pr_ReadEntry(at, 0, pos, ¢ry); if (code) return code; code = AddToEntry(at, ¢ry, pos, *aid); if (code) return code; /* and now the user entry */ pos = FindByID(at, *aid); if (!pos) return PRBADNAM; code = pr_ReadEntry(at, 0, pos, &tentry); if (code) return code; code = AddToEntry(at, &tentry, pos, tentry.cellid); if (code) return code; return PRSUCCESS; } /* Remember the largest group id or largest user id */ if (flag & PRGRP) { /* group ids are negative */ if (tentry.id < (afs_int32) ntohl(cheader.maxGroup)) { code = set_header_word(at, maxGroup, htonl(tentry.id)); if (code) return PRDBFAIL; } } else { if (tentry.id > (afs_int32) ntohl(cheader.maxID)) { code = set_header_word(at, maxID, htonl(tentry.id)); if (code) return PRDBFAIL; } } /* Charge the creator for this group */ if (flag & PRGRP) { afs_int32 loc = FindByID(at, creator); struct prentry centry; int admin; if (loc) { /* this should only fail during initialization */ code = pr_Read(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; /* If quota is uninitialized, do it */ if (!(ntohl(centry.flags) & PRQUOTA)) { centry.flags = htonl(ntohl(centry.flags) | PRQUOTA); centry.ngroups = centry.nusers = htonl(20); } /* Admins don't get charged for creating a group. * If in noAuth mode, you get changed for it but you * are still allowed to create as many groups as you want. */ admin = ((creator == SYSADMINID) || IsAMemberOf(at, creator, SYSADMINID)); if (!admin) { if (ntohl(centry.ngroups) <= 0) { if (!pr_noAuth) return PRNOMORE; } else { centry.ngroups = htonl(ntohl(centry.ngroups) - 1); } } code = pr_Write(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; } /* if (loc) */ } else { /* Initialize the quota for the user. Groups don't have their * quota initialized. */ tentry.flags |= PRQUOTA; tentry.ngroups = tentry.nusers = 20; } if (creator == 0) tentry.creator = tentry.id; else tentry.creator = creator; *aid = tentry.id; code = pr_WriteEntry(at, 0, newEntry, &tentry); if (code) return PRDBFAIL; code = AddToIDHash(at, *aid, newEntry); if (code != PRSUCCESS) return code; code = AddToNameHash(at, aname, newEntry); if (code != PRSUCCESS) return code; if (tentry.flags & PRGRP) { code = AddToOwnerChain(at, tentry.id, oid); if (code) return code; } if (tentry.flags & PRGRP) { if (inc_header_word(at, groupcount, 1)) return PRDBFAIL; } else if (tentry.flags & PRINST) { if (inc_header_word(at, instcount, 1)) return PRDBFAIL; } else { if (inc_header_word(at, usercount, 1)) return PRDBFAIL; } return PRSUCCESS; } /* RemoveFromEntry - remove aid from bid's entries list, freeing a continuation * entry if appropriate */ afs_int32 RemoveFromEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 bid) { afs_int32 code; struct prentry tentry; struct contentry centry; struct contentry hentry; afs_int32 temp; afs_int32 i, j; afs_int32 nptr; afs_int32 hloc; if (aid == bid) return PRINCONSISTENT; memset(&hentry, 0, sizeof(hentry)); temp = FindByID(at, bid); if (temp == 0) return PRNOENT; code = pr_ReadEntry(at, 0, temp, &tentry); if (code != 0) return code; tentry.removeTime = time(0); for (i = 0; i < PRSIZE; i++) { if (tentry.entries[i] == aid) { tentry.entries[i] = PRBADID; tentry.count--; code = pr_WriteEntry(at, 0, temp, &tentry); if (code != 0) return code; return PRSUCCESS; } if (tentry.entries[i] == 0) /* found end of list */ return PRNOENT; } hloc = 0; nptr = tentry.next; while (nptr != 0) { code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; if ((centry.id != bid) || !(centry.flags & PRCONT)) return PRDBBAD; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == aid) { centry.entries[i] = PRBADID; for (j = 0; j < COSIZE; j++) if (centry.entries[j] != PRBADID && centry.entries[j] != 0) break; if (j == COSIZE) { /* can free this block */ if (hloc == 0) { tentry.next = centry.next; } else { hentry.next = centry.next; code = pr_WriteCoEntry(at, 0, hloc, &hentry); if (code != 0) return code; } code = FreeBlock(at, nptr); if (code) return code; } else { /* can't free it yet */ code = pr_WriteCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; } tentry.count--; code = pr_WriteEntry(at, 0, temp, &tentry); if (code) return PRDBFAIL; return 0; } if (centry.entries[i] == 0) return PRNOENT; } /* for all coentry slots */ hloc = nptr; nptr = centry.next; memcpy(&hentry, ¢ry, sizeof(centry)); } /* while there are coentries */ return PRNOENT; } #if defined(SUPERGROUPS) /* ChangeIDEntry - remove aid from bid's entries list, freeing a continuation * entry if appropriate */ afs_int32 ChangeIDEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 newid, afs_int32 bid) { afs_int32 code; struct prentry tentry; struct contentry centry; afs_int32 temp; afs_int32 i, j; afs_int32 nptr; if (aid == bid) return PRINCONSISTENT; temp = FindByID(at, bid); if (temp == 0) { return PRNOENT; } code = pr_ReadEntry(at, 0, temp, &tentry); if (code != 0) return code; for (i = 0; i < PRSIZE; i++) { if (tentry.entries[i] == aid) { tentry.entries[i] = newid; code = pr_WriteEntry(at, 0, temp, &tentry); if (code != 0) return code; return PRSUCCESS; } if (tentry.entries[i] == 0) { /* found end of list */ return PRNOENT; } } nptr = tentry.next; while (nptr) { code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; if ((centry.id != bid) || !(centry.flags & PRCONT)) { fprintf(stderr, "ChangeIDEntry: bad database bid=%d centry.id=%d .flags=%d\n", bid, centry.id, centry.flags); return PRDBBAD; } for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == aid) { centry.entries[i] = newid; for (j = 0; j < COSIZE; j++) if (centry.entries[j] != PRBADID && centry.entries[j] != 0) break; code = pr_WriteCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; return 0; } if (centry.entries[i] == 0) { return PRNOENT; } } /* for all coentry slots */ nptr = centry.next; } /* while there are coentries */ return PRNOENT; } /* #ifdef SUPERGROUPS */ /* RemoveFromSGEntry - remove aid from bid's supergroups list, freeing a * continuation entry if appropriate */ afs_int32 RemoveFromSGEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 bid) { afs_int32 code; struct prentry tentry; struct prentryg *tentryg; struct contentry centry; struct contentry hentry; afs_int32 temp; afs_int32 i, j; afs_int32 nptr; afs_int32 hloc; if (aid == bid) return PRINCONSISTENT; memset(&hentry, 0, sizeof(hentry)); temp = FindByID(at, bid); if (temp == 0) { return PRNOENT; } code = pr_ReadEntry(at, 0, temp, &tentry); if (code != 0) return code; tentry.removeTime = time(NULL); tentryg = (struct prentryg *)&tentry; for (i = 0; i < SGSIZE; i++) { if (tentryg->supergroup[i] == aid) { tentryg->supergroup[i] = PRBADID; tentryg->countsg--; code = pr_WriteEntry(at, 0, temp, &tentry); if (code != 0) return code; return PRSUCCESS; } if (tentryg->supergroup[i] == 0) { /* found end of list */ return PRNOENT; } } hloc = 0; nptr = tentryg->nextsg; while (nptr) { code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; if ((centry.id != bid) || !(centry.flags & PRCONT)) { fprintf(stderr, "ChangeIDEntry: bad database bid=%d centry.id=%d .flags=%d\n", bid, centry.id, centry.flags); return PRDBBAD; } for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == aid) { centry.entries[i] = PRBADID; for (j = 0; j < COSIZE; j++) if (centry.entries[j] != PRBADID && centry.entries[j] != 0) break; if (j == COSIZE) { /* can free this block */ if (hloc == 0) { tentryg->nextsg = centry.next; } else { hentry.next = centry.next; code = pr_WriteCoEntry(at, 0, hloc, &hentry); if (code != 0) return code; } code = FreeBlock(at, nptr); if (code) return code; } else { /* can't free it yet */ code = pr_WriteCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; } tentryg->countsg--; code = pr_WriteEntry(at, 0, temp, &tentry); if (code) return PRDBFAIL; return 0; } if (centry.entries[i] == 0) { return PRNOENT; } } /* for all coentry slots */ hloc = nptr; nptr = centry.next; memcpy(&hentry, ¢ry, sizeof(centry)); } /* while there are coentries */ return PRNOENT; } #endif /* SUPERGROUPS */ /* DeleteEntry - delete the entry in tentry at loc, removing it from all * groups, putting groups owned by it on orphan chain, and freeing the space */ afs_int32 DeleteEntry(struct ubik_trans *at, struct prentry *tentry, afs_int32 loc) { afs_int32 code; struct contentry centry; afs_int32 i; afs_int32 nptr; if (strchr(tentry->name, '@')) { if (tentry->flags & PRGRP) { /* If there are still foreign user accounts from that cell * don't delete the group */ if (tentry->count) return PRBADARG; } else { /* adjust quota */ afs_int32 loc = FindByID(at, tentry->cellid); struct prentry centry; if (loc) { code = pr_Read(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; if (ntohl(centry.flags) & PRQUOTA) { centry.ngroups = htonl(ntohl(centry.ngroups) + 1); } code = pr_Write(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; } } } /* First remove the entire membership list */ for (i = 0; i < PRSIZE; i++) { if (tentry->entries[i] == PRBADID) continue; if (tentry->entries[i] == 0) break; #if defined(SUPERGROUPS) if ((tentry->flags & PRGRP) && tentry->entries[i] < 0) /* Supergroup */ code = RemoveFromSGEntry(at, tentry->id, tentry->entries[i]); else #endif code = RemoveFromEntry(at, tentry->id, tentry->entries[i]); if (code) return code; } #if defined(SUPERGROUPS) { struct prentryg *tentryg = (struct prentryg *)tentry; /* Then remove the entire supergroup list */ for (i = 0; i < SGSIZE; i++) { if (tentryg->supergroup[i] == PRBADID) continue; if (tentryg->supergroup[i] == 0) break; code = RemoveFromEntry(at, tentry->id, tentryg->supergroup[i]); if (code) return code; } } #endif /* SUPERGROUPS */ nptr = tentry->next; while (nptr != 0) { code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return PRDBFAIL; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; code = RemoveFromEntry(at, tentry->id, centry.entries[i]); if (code) return code; } code = FreeBlock(at, nptr); /* free continuation block */ if (code) return code; nptr = centry.next; } /* Remove us from other's owned chain. Note that this will zero our owned * field (on disk) so this step must follow the above step in case we are * on our own owned list. */ if (tentry->flags & PRGRP) { if (tentry->owner) { code = RemoveFromOwnerChain(at, tentry->id, tentry->owner); if (code) return code; } else { code = RemoveFromOrphan(at, tentry->id); if (code) return code; } } code = RemoveFromIDHash(at, tentry->id, &loc); if (code != PRSUCCESS) return code; code = RemoveFromNameHash(at, tentry->name, &loc); if (code != PRSUCCESS) return code; if (tentry->flags & PRGRP) { afs_int32 loc = FindByID(at, tentry->creator); struct prentry centry; int admin; if (loc) { code = pr_Read(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; admin = ((tentry->creator == SYSADMINID) || IsAMemberOf(at, tentry->creator, SYSADMINID)); if (ntohl(centry.flags) & PRQUOTA) { if (!(admin && (ntohl(centry.ngroups) >= 20))) { centry.ngroups = htonl(ntohl(centry.ngroups) + 1); } } code = pr_Write(at, 0, loc, ¢ry, sizeof(centry)); if (code) return code; } } if (tentry->flags & PRGRP) { if (inc_header_word(at, groupcount, -1)) return PRDBFAIL; } else if (tentry->flags & PRINST) { if (inc_header_word(at, instcount, -1)) return PRDBFAIL; } else { if (strchr(tentry->name, '@')) { if (inc_header_word(at, foreigncount, -1)) return PRDBFAIL; } else { if (inc_header_word(at, usercount, -1)) return PRDBFAIL; } } code = FreeBlock(at, loc); return code; } /* AddToEntry - add aid to entry's entries list, alloc'ing a continuation block * if needed. * * Note the entry is written out by this routine. */ afs_int32 AddToEntry(struct ubik_trans *tt, struct prentry *entry, afs_int32 loc, afs_int32 aid) { afs_int32 code; afs_int32 i; struct contentry nentry; struct contentry aentry; afs_int32 nptr; afs_int32 last; /* addr of last cont. block */ afs_int32 first = 0; afs_int32 cloc = 0; afs_int32 slot = -1; if (entry->id == aid) return PRINCONSISTENT; entry->addTime = time(0); for (i = 0; i < PRSIZE; i++) { if (entry->entries[i] == aid) return PRIDEXIST; if (entry->entries[i] == PRBADID) { /* remember this spot */ first = 1; slot = i; } else if (entry->entries[i] == 0) { /* end of the line */ if (slot == -1) { first = 1; slot = i; } break; } } last = 0; nptr = entry->next; while (nptr != 0) { code = pr_ReadCoEntry(tt, 0, nptr, &nentry); if (code != 0) return code; last = nptr; if (!(nentry.flags & PRCONT)) return PRDBFAIL; for (i = 0; i < COSIZE; i++) { if (nentry.entries[i] == aid) return PRIDEXIST; if (nentry.entries[i] == PRBADID) { if (slot == -1) { slot = i; cloc = nptr; } } else if (nentry.entries[i] == 0) { if (slot == -1) { slot = i; cloc = nptr; } break; } } nptr = nentry.next; } if (slot != -1) { /* we found a place */ entry->count++; if (first) { /* place is in first block */ entry->entries[slot] = aid; code = pr_WriteEntry(tt, 0, loc, entry); if (code != 0) return code; return PRSUCCESS; } code = pr_WriteEntry(tt, 0, loc, entry); if (code) return code; code = pr_ReadCoEntry(tt, 0, cloc, &aentry); if (code != 0) return code; aentry.entries[slot] = aid; code = pr_WriteCoEntry(tt, 0, cloc, &aentry); if (code != 0) return code; return PRSUCCESS; } /* have to allocate a continuation block if we got here */ nptr = AllocBlock(tt); if (last) { /* then we should tack new block after last block in cont. chain */ nentry.next = nptr; code = pr_WriteCoEntry(tt, 0, last, &nentry); if (code != 0) return code; } else { entry->next = nptr; } memset(&aentry, 0, sizeof(aentry)); aentry.flags |= PRCONT; aentry.id = entry->id; aentry.next = 0; aentry.entries[0] = aid; code = pr_WriteCoEntry(tt, 0, nptr, &aentry); if (code != 0) return code; /* don't forget to update count, here! */ entry->count++; code = pr_WriteEntry(tt, 0, loc, entry); return code; } #if defined(SUPERGROUPS) /* AddToSGEntry - add aid to entry's supergroup list, alloc'ing a * continuation block if needed. * * Note the entry is written out by this routine. */ afs_int32 AddToSGEntry(struct ubik_trans *tt, struct prentry *entry, afs_int32 loc, afs_int32 aid) { afs_int32 code; afs_int32 i; struct contentry nentry; struct contentry aentry; struct prentryg *entryg; afs_int32 nptr; afs_int32 last; /* addr of last cont. block */ afs_int32 first = 0; afs_int32 cloc = 0; afs_int32 slot = -1; if (entry->id == aid) return PRINCONSISTENT; entry->addTime = time(NULL); entryg = (struct prentryg *)entry; for (i = 0; i < SGSIZE; i++) { if (entryg->supergroup[i] == aid) return PRIDEXIST; if (entryg->supergroup[i] == PRBADID) { /* remember this spot */ first = 1; slot = i; } else if (entryg->supergroup[i] == 0) { /* end of the line */ if (slot == -1) { first = 1; slot = i; } break; } } last = 0; nptr = entryg->nextsg; while (nptr) { code = pr_ReadCoEntry(tt, 0, nptr, &nentry); if (code != 0) return code; last = nptr; if (!(nentry.flags & PRCONT)) return PRDBFAIL; for (i = 0; i < COSIZE; i++) { if (nentry.entries[i] == aid) return PRIDEXIST; if (nentry.entries[i] == PRBADID) { if (slot == -1) { slot = i; cloc = nptr; } } else if (nentry.entries[i] == 0) { if (slot == -1) { slot = i; cloc = nptr; } break; } } nptr = nentry.next; } if (slot != -1) { /* we found a place */ entryg->countsg++; if (first) { /* place is in first block */ entryg->supergroup[slot] = aid; code = pr_WriteEntry(tt, 0, loc, entry); if (code != 0) return code; return PRSUCCESS; } code = pr_WriteEntry(tt, 0, loc, entry); if (code) return code; code = pr_ReadCoEntry(tt, 0, cloc, &aentry); if (code != 0) return code; aentry.entries[slot] = aid; code = pr_WriteCoEntry(tt, 0, cloc, &aentry); if (code != 0) return code; return PRSUCCESS; } /* have to allocate a continuation block if we got here */ nptr = AllocBlock(tt); if (last) { /* then we should tack new block after last block in cont. chain */ nentry.next = nptr; code = pr_WriteCoEntry(tt, 0, last, &nentry); if (code != 0) return code; } else { entryg->nextsg = nptr; } memset(&aentry, 0, sizeof(aentry)); aentry.flags |= PRCONT; aentry.id = entry->id; aentry.next = 0; aentry.entries[0] = aid; code = pr_WriteCoEntry(tt, 0, nptr, &aentry); if (code != 0) return code; /* don't forget to update count, here! */ entryg->countsg++; code = pr_WriteEntry(tt, 0, loc, entry); return code; } #endif /* SUPERGROUPS */ afs_int32 AddToPRList(prlist *alist, int *sizeP, afs_int32 id) { afs_int32 *tmp; int count; if (alist->prlist_len >= *sizeP) { count = alist->prlist_len + 100; if (alist->prlist_val) { tmp = realloc(alist->prlist_val, count * sizeof(afs_int32)); } else { tmp = malloc(count * sizeof(afs_int32)); } if (!tmp) return (PRNOMEM); alist->prlist_val = tmp; *sizeP = count; } alist->prlist_val[alist->prlist_len++] = id; return 0; } afs_int32 GetList(struct ubik_trans *at, struct prentry *tentry, prlist *alist, afs_int32 add) { afs_int32 code; afs_int32 i; struct contentry centry; afs_int32 nptr; int size; int count = 0; size = 0; alist->prlist_val = 0; alist->prlist_len = 0; for (i = 0; i < PRSIZE; i++) { if (tentry->entries[i] == PRBADID) continue; if (tentry->entries[i] == 0) break; code = AddToPRList(alist, &size, tentry->entries[i]); if (code) return code; #if defined(SUPERGROUPS) if (!add) continue; code = GetListSG2(at, tentry->entries[i], alist, &size, depthsg); if (code) return code; #endif } for (nptr = tentry->next; nptr != 0; nptr = centry.next) { /* look through cont entries */ code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; code = AddToPRList(alist, &size, centry.entries[i]); if (code) return code; #if defined(SUPERGROUPS) if (!add) continue; code = GetListSG2(at, centry.entries[i], alist, &size, depthsg); if (code) return code; #endif } if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } if (add) { /* this is for a CPS, so tack on appropriate stuff */ if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) { if ((code = AddToPRList(alist, &size, ANYUSERID)) || (code = AddAuthGroup(tentry, alist, &size)) || (code = AddToPRList(alist, &size, tentry->id))) return code; } else { if ((code = AddToPRList(alist, &size, ANYUSERID)) || (code = AddToPRList(alist, &size, tentry->id))) return code; } } #ifndef AFS_PTHREAD_ENV if (alist->prlist_len > 100) IOMGR_Poll(); #endif qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp); return PRSUCCESS; } afs_int32 GetList2(struct ubik_trans *at, struct prentry *tentry, struct prentry *tentry2, prlist *alist, afs_int32 add) { afs_int32 code = 0; afs_int32 i; struct contentry centry; afs_int32 nptr; afs_int32 size; int count = 0; size = 0; alist->prlist_val = 0; alist->prlist_len = 0; for (i = 0; i < PRSIZE; i++) { if (tentry->entries[i] == PRBADID) continue; if (tentry->entries[i] == 0) break; code = AddToPRList(alist, &size, tentry->entries[i]); if (code) return code; #if defined(SUPERGROUPS) if (!add) continue; code = GetListSG2(at, tentry->entries[i], alist, &size, depthsg); if (code) return code; #endif } nptr = tentry->next; while (nptr != 0) { /* look through cont entries */ code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; code = AddToPRList(alist, &size, centry.entries[i]); if (code) return code; #if defined(SUPERGROUPS) if (!add) continue; code = GetListSG2(at, centry.entries[i], alist, &size, depthsg); if (code) return code; #endif } nptr = centry.next; if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } for (i = 0; i < PRSIZE; i++) { if (tentry2->entries[i] == PRBADID) continue; if (tentry2->entries[i] == 0) break; code = AddToPRList(alist, &size, tentry2->entries[i]); if (code) break; } if (!code) { nptr = tentry2->next; while (nptr != 0) { /* look through cont entries */ code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) break; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; code = AddToPRList(alist, &size, centry.entries[i]); if (code) break; } nptr = centry.next; if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } } if (add) { /* this is for a CPS, so tack on appropriate stuff */ if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) { if ((code = AddToPRList(alist, &size, ANYUSERID)) || (code = AddToPRList(alist, &size, AUTHUSERID)) || (code = AddToPRList(alist, &size, tentry->id))) return code; } else { if ((code = AddToPRList(alist, &size, ANYUSERID)) || (code = AddToPRList(alist, &size, tentry->id))) return code; } } #ifndef AFS_PTHREAD_ENV if (alist->prlist_len > 100) IOMGR_Poll(); #endif qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp); return PRSUCCESS; } #if defined(SUPERGROUPS) afs_int32 GetListSG2(struct ubik_trans *at, afs_int32 gid, prlist *alist, afs_int32 *sizeP, afs_int32 depth) { afs_int32 code; struct prentry tentry; struct prentryg *tentryg = (struct prentryg *)&tentry; afs_int32 i; struct contentry centry; afs_int32 nptr; int count = 0; afs_int32 temp; int didsomething; #if DEBUG_SG_MAP int predictfound, predictflagged; #endif #if DEBUG_SG_MAP predictfound = 0; if (!in_map(sg_flagged, -gid)) { predictflagged = 0; fprintf(stderr, "GetListSG2: I have not yet searched for gid=%d\n", gid); } else if (predictflagged = 1, in_map(sg_found, -gid)) { predictfound = 1; fprintf(stderr, "GetListSG2: I have already searched for gid=%d, and predict success.\n", gid); } #endif if (in_map(sg_flagged, -gid) && !in_map(sg_found, -gid)) { #if DEBUG_SG_MAP fprintf(stderr, "GetListSG2: I have already searched for gid=%d, and predict failure.\n", gid); #else return 0; #endif } if (depth < 1) return 0; temp = FindByID(at, gid); if (!temp) { code = PRNOENT; return code; } code = pr_ReadEntry(at, 0, temp, &tentry); if (code) return code; #if DEBUG_SG_MAP fprintf(stderr, "GetListSG2: lookup for gid=%d [\n", gid); #endif didsomething = 0; for (i = 0; i < SGSIZE; i++) { if (tentryg->supergroup[i] == PRBADID) continue; if (tentryg->supergroup[i] == 0) break; didsomething = 1; #if DEBUG_SG_MAP fprintf(stderr, "via gid=%d, added %d\n", gid, e.tentryg.supergroup[i]); #endif code = AddToPRList(alist, sizeP, tentryg->supergroup[i]); if (code) return code; code = GetListSG2(at, tentryg->supergroup[i], alist, sizeP, depth - 1); if (code) return code; } nptr = tentryg->nextsg; while (nptr) { didsomething = 1; /* look through cont entries */ code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; #if DEBUG_SG_MAP fprintf(stderr, "via gid=%d, added %d\n", gid, e.centry.entries[i]); #endif code = AddToPRList(alist, sizeP, centry.entries[i]); if (code) return code; code = GetListSG2(at, centry.entries[i], alist, sizeP, depth - 1); if (code) return code; } nptr = centry.next; if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } #if DEBUG_SG_MAP fprintf(stderr, "] for gid %d, done [flag=%s]\n", gid, didsomething ? "TRUE" : "FALSE"); if (predictflagged && didsomething != predictfound) fprintf(stderr, "**** for gid=%d, didsomething=%d predictfound=%d\n", gid, didsomething, predictfound); #endif if (didsomething) sg_found = add_map(sg_found, -gid); else sg_found = bic_map(sg_found, add_map(NIL_MAP, -gid)); sg_flagged = add_map(sg_flagged, -gid); return 0; } afs_int32 GetSGList(struct ubik_trans *at, struct prentry *tentry, prlist *alist) { afs_int32 code; afs_int32 i; struct contentry centry; struct prentryg *tentryg; afs_int32 nptr; int size; int count = 0; size = 0; alist->prlist_val = 0; alist->prlist_len = 0; tentryg = (struct prentryg *)tentry; for (i = 0; i < SGSIZE; i++) { if (tentryg->supergroup[i] == PRBADID) continue; if (tentryg->supergroup[i] == 0) break; code = AddToPRList(alist, &size, tentryg->supergroup[i]); if (code) return code; } nptr = tentryg->nextsg; while (nptr) { /* look through cont entries */ code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code != 0) return code; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; code = AddToPRList(alist, &size, centry.entries[i]); if (code) return code; } nptr = centry.next; if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } #ifndef AFS_PTHREAD_ENV if (alist->prlist_len > 100) IOMGR_Poll(); #endif qsort((char *)alist->prlist_val, (int)alist->prlist_len, sizeof(afs_int32), IDCmp); return PRSUCCESS; } #endif /* SUPERGROUPS */ afs_int32 GetOwnedChain(struct ubik_trans *ut, afs_int32 *next, prlist *alist) { afs_int32 code; struct prentry tentry; int size; int count = 0; size = 0; alist->prlist_val = 0; alist->prlist_len = 0; for (; *next; *next = ntohl(tentry.nextOwned)) { code = pr_Read(ut, 0, *next, &tentry, sizeof(tentry)); if (code) return code; code = AddToPRList(alist, &size, ntohl(tentry.id)); if (alist->prlist_len >= PR_MAXGROUPS) { return PRTOOMANY; } if (code) return code; if (count++ > 50) { #ifndef AFS_PTHREAD_ENV IOMGR_Poll(); #endif count = 0; } } #ifndef AFS_PTHREAD_ENV if (alist->prlist_len > 100) IOMGR_Poll(); #endif qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp); return PRSUCCESS; } afs_int32 GetMax(struct ubik_trans *at, afs_int32 *uid, afs_int32 *gid) { *uid = ntohl(cheader.maxID); *gid = ntohl(cheader.maxGroup); return PRSUCCESS; } afs_int32 SetMax(struct ubik_trans *at, afs_int32 id, afs_int32 flag) { afs_int32 code; if (flag & PRGRP) { cheader.maxGroup = htonl(id); code = pr_Write(at, 0, 16, (char *)&cheader.maxGroup, sizeof(cheader.maxGroup)); if (code != 0) return code; } else { cheader.maxID = htonl(id); code = pr_Write(at, 0, 20, (char *)&cheader.maxID, sizeof(cheader.maxID)); if (code != 0) return code; } return PRSUCCESS; } static afs_int32 UpdateCache(struct ubik_trans *tt, void *rock) { afs_int32 code; code = pr_Read(tt, 0, 0, (char *)&cheader, sizeof(cheader)); if (code != 0) { afs_com_err(whoami, code, "Couldn't read header"); } return code; } afs_int32 read_DbHeader(struct ubik_trans *tt) { return ubik_CheckCache(tt, UpdateCache, NULL); } int pr_noAuth; /** * reads in db cache from ubik. * * @param[in] ut ubik transaction * @param[out] rock opaque pointer to an int*, which on success will be set * to 1 if we need to build the database, or 0 if we do not * * @return operation status * @retval 0 success */ static afs_int32 Initdb_check(struct ubik_trans *tt, void *rock) { int *build_rock = rock; afs_int32 code; afs_int32 len; len = sizeof(cheader); code = pr_Read(tt, 0, 0, (char *)&cheader, len); if (code != 0) { afs_com_err(whoami, code, "couldn't read header"); return code; } if ((ntohl(cheader.version) == PRDBVERSION) && ntohl(cheader.headerSize) == sizeof(cheader) && ntohl(cheader.eofPtr) != 0 && FindByID(tt, ANONYMOUSID) != 0) { /* database exists, so we don't have to build it */ *build_rock = 0; return 0; } /* else we need to build a database */ *build_rock = 1; return 0; } afs_int32 Initdb(void) { struct ubik_trans *tt; int build = 0; afs_int32 code; /* init the database. We'll try reading it, but if we're starting * from scratch, we'll have to do a write transaction. */ pr_noAuth = afsconf_GetNoAuthFlag(prdir); code = ubik_BeginTransReadAny(dbase, UBIK_READTRANS, &tt); if (code) return code; code = ubik_SetLock(tt, 1, 1, LOCKREAD); if (code) { ubik_AbortTrans(tt); return code; } code = ubik_CheckCache(tt, Initdb_check, &build); if (code) { ubik_AbortTrans(tt); return code; } if (build) { /* Only rebuild database if the db was deleted (the header is zero) */ char *bp = (char *)&cheader; int i; for (i = 0; i < sizeof(cheader); i++) { if (bp[i]) { code = PRDBBAD; afs_com_err(whoami, code, "Can't rebuild database because it is not empty"); break; } } } if (code) { ubik_EndTrans(tt); } else { code = ubik_EndTrans(tt); } if (code || !build) { /* either we encountered an error, or we don't need to build the db */ return code; } code = ubik_BeginTrans(dbase, UBIK_WRITETRANS, &tt); if (code) return code; code = ubik_SetLock(tt, 1, 1, LOCKWRITE); if (code) { ubik_AbortTrans(tt); return code; } /* before doing a rebuild, check again that the dbase looks bad, because * the previous check was only under a ReadAny transaction, and there could * actually have been a good database out there. Now that we have a * real write transaction, make sure things are still bad. */ code = pr_Read(tt, 0, 0, (char *)&cheader, sizeof(cheader)); if (code != 0) { afs_com_err(whoami, code, "couldn't read header"); ubik_AbortTrans(tt); return code; } if ((ntohl(cheader.version) == PRDBVERSION) && ntohl(cheader.headerSize) == sizeof(cheader) && ntohl(cheader.eofPtr) != 0 && FindByID(tt, ANONYMOUSID) != 0) { /* database exists, so we don't have to build it */ code = ubik_EndTrans(tt); if (code) return code; return PRSUCCESS; } /* Initialize the database header */ if ((code = set_header_word(tt, version, htonl(PRDBVERSION))) || (code = set_header_word(tt, headerSize, htonl(sizeof(cheader)))) || (code = set_header_word(tt, eofPtr, cheader.headerSize))) { afs_com_err(whoami, code, "couldn't write header words"); ubik_AbortTrans(tt); return code; } #define InitialGroup(id,name) do { \ afs_int32 temp = (id); \ afs_int32 flag = (id) < 0 ? PRGRP : 0; \ code = CreateEntry \ (tt, (name), &temp, /*idflag*/1, flag, SYSADMINID, SYSADMINID); \ if (code) { \ afs_com_err (whoami, code, "couldn't create %s with id %di.", \ (name), (id)); \ ubik_AbortTrans(tt); \ return code; \ } \ } while (0) InitialGroup(SYSADMINID, "system:administrators"); InitialGroup(SYSBACKUPID, "system:backup"); InitialGroup(ANYUSERID, "system:anyuser"); InitialGroup(AUTHUSERID, "system:authuser"); InitialGroup(SYSVIEWERID, "system:ptsviewers"); InitialGroup(ANONYMOUSID, "anonymous"); /* Well, we don't really want the max id set to anonymousid, so we'll set * it back to 0 */ code = set_header_word(tt, maxID, 0); /* correct in any byte order */ if (code) { afs_com_err(whoami, code, "couldn't reset max id"); ubik_AbortTrans(tt); return code; } code = ubik_EndTrans(tt); if (code) return code; return PRSUCCESS; } afs_int32 ChangeEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 cid, char *name, afs_int32 oid, afs_int32 newid) { afs_int32 code; afs_int32 i, pos; #if defined(SUPERGROUPS) afs_int32 nextpos; #else afs_int32 nptr; struct contentry centry; #endif struct prentry tentry, tent; afs_int32 loc; afs_int32 oldowner; afs_int32 admin; char holder[PR_MAXNAMELEN]; char temp[PR_MAXNAMELEN]; char oldname[PR_MAXNAMELEN]; char *atsign; memset(holder, 0, PR_MAXNAMELEN); memset(temp, 0, PR_MAXNAMELEN); loc = FindByID(at, aid); if (!loc) return PRNOENT; code = pr_ReadEntry(at, 0, loc, &tentry); if (code) return PRDBFAIL; if (restricted && !IsAMemberOf(at, cid, SYSADMINID)) return PRPERM; if (tentry.owner != cid && !IsAMemberOf(at, cid, SYSADMINID) && !IsAMemberOf(at, cid, tentry.owner) && !pr_noAuth) return PRPERM; tentry.changeTime = time(0); admin = pr_noAuth || IsAMemberOf(at, cid, SYSADMINID); /* we're actually trying to change the id */ if (newid && (newid != aid)) { if (!admin) return PRPERM; pos = FindByID(at, newid); if (pos) return PRIDEXIST; /* new id already in use! */ if ((aid < 0 && newid > 0) || (aid > 0 && newid < 0)) return PRPERM; /* Should check that foreign users id to change to is good: inRange() */ /* if new id is not in use, rehash things */ code = RemoveFromIDHash(at, aid, &loc); if (code != PRSUCCESS) return code; tentry.id = newid; code = pr_WriteEntry(at, 0, loc, &tentry); if (code) return code; code = AddToIDHash(at, tentry.id, loc); if (code) return code; /* get current data */ code = pr_ReadEntry(at, 0, loc, &tentry); if (code) return PRDBFAIL; #if defined(SUPERGROUPS) if (tentry.id > (afs_int32) ntohl(cheader.maxID)) code = set_header_word(at, maxID, htonl(tentry.id)); if (code) return PRDBFAIL; /* need to fix up: membership * (supergroups?) * ownership */ for (i = 0; i < PRSIZE; i++) { if (tentry.entries[i] == PRBADID) continue; if (tentry.entries[i] == 0) break; if ((tentry.flags & PRGRP) && tentry.entries[i] < 0) { /* Supergroup */ return 5; /* not yet, in short. */ } else { code = ChangeIDEntry(at, aid, newid, tentry.entries[i]); } if (code) return code; } for (pos = ntohl(tentry.owned); pos; pos = nextpos) { code = pr_ReadEntry(at, 0, pos, &tent); if (code) break; tent.owner = newid; nextpos = tent.nextOwned; code = pr_WriteEntry(at, 0, pos, &tent); if (code) break; } pos = tentry.next; while (pos) { #define centry (*(struct contentry*)&tent) code = pr_ReadCoEntry(at, 0, pos, ¢ry); if ((centry.id != aid) || !(centry.flags & PRCONT)) { fprintf(stderr, "ChangeEntry: bad database aid=%d centry.id=%d .flags=%d\n", aid, centry.id, centry.flags); return PRDBBAD; } centry.id = newid; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; if ((centry.flags & PRGRP) && centry.entries[i] < 0) { /* Supergroup */ return 5; /* not yet, in short. */ } else { code = ChangeIDEntry(at, aid, newid, centry.entries[i]); } if (code) return code; } code = pr_WriteCoEntry(at, 0, pos, ¢ry); pos = centry.next; #undef centry } if (code) return code; #else /* SUPERGROUPS */ /* Also change the references from the membership list */ for (i = 0; i < PRSIZE; i++) { if (tentry.entries[i] == PRBADID) continue; if (tentry.entries[i] == 0) break; pos = FindByID(at, tentry.entries[i]); if (!pos) return (PRDBFAIL); code = RemoveFromEntry(at, aid, tentry.entries[i]); if (code) return code; code = pr_ReadEntry(at, 0, pos, &tent); if (code) return code; code = AddToEntry(at, &tent, pos, newid); if (code) return code; } /* Look through cont entries too. This needs to be broken into * seperate transaction so that no one transaction becomes too * large to complete. */ for (nptr = tentry.next; nptr; nptr = centry.next) { code = pr_ReadCoEntry(at, 0, nptr, ¢ry); if (code) return code; for (i = 0; i < COSIZE; i++) { if (centry.entries[i] == PRBADID) continue; if (centry.entries[i] == 0) break; pos = FindByID(at, centry.entries[i]); if (!pos) return (PRDBFAIL); code = RemoveFromEntry(at, aid, centry.entries[i]); if (code) return code; code = pr_ReadEntry(at, 0, pos, &tent); if (code) return code; code = AddToEntry(at, &tent, pos, newid); if (code) return code; } } #endif /* SUPERGROUPS */ } atsign = strchr(tentry.name, '@'); /* check for foreign entry */ /* Change the owner */ if (oid && (oid != tentry.owner)) { /* only groups can have their owner's changed */ if (!(tentry.flags & PRGRP)) return PRPERM; if (atsign != NULL) return PRPERM; oldowner = tentry.owner; tentry.owner = oid; /* The entry must be written through first so Remove and Add routines * can operate on disk data */ code = pr_WriteEntry(at, 0, loc, &tentry); if (code) return PRDBFAIL; /* switch owner chains */ if (oldowner) /* if it has an owner */ code = RemoveFromOwnerChain(at, tentry.id, oldowner); else /* must be an orphan */ code = RemoveFromOrphan(at, tentry.id); if (code) return code; code = AddToOwnerChain(at, tentry.id, tentry.owner); if (code) return code; /* fix up the name */ if (strlen(name) == 0) name = tentry.name; /* get current data */ code = pr_ReadEntry(at, 0, loc, &tentry); if (code) return PRDBFAIL; } /* Change the name, if name is a ptr to tentry.name then this name change * is due to a chown, otherwise caller has specified a new name */ if ((name == tentry.name) || (*name && (strcmp(tentry.name, name) != 0))) { strncpy(oldname, tentry.name, PR_MAXNAMELEN); if (tentry.flags & PRGRP) { /* don't let foreign cell groups change name */ if (atsign != NULL) return PRPERM; if (tentry.owner == 0 || tentry.owner == ANONYMOUSID) tentry.owner = cid; code = CorrectGroupName(at, name, cid, tentry.owner, admin, tentry.name); if (code) return code; if (name == tentry.name) { /* owner fixup */ if (strcmp(oldname, tentry.name) == 0) goto nameOK; } else { /* new name, caller must be correct */ if (strcmp(name, tentry.name) != 0) return PRBADNAM; } } else /* Allow a foreign name change only if the cellname part is * the same */ { char *newatsign; newatsign = strchr(name, '@'); if (newatsign != atsign) { /* if they are the same no problem */ /*if the pointers are not equal the strings better be */ if ((atsign == NULL) || (newatsign == NULL) || strcmp(atsign, newatsign)) return PRPERM; } if (!CorrectUserName(name)) return PRBADNAM; } pos = FindByName(at, name, &tent); if (pos) return PREXIST; code = RemoveFromNameHash(at, oldname, &loc); if (code != PRSUCCESS) return code; strncpy(tentry.name, name, PR_MAXNAMELEN); code = pr_WriteEntry(at, 0, loc, &tentry); if (code) return PRDBFAIL; code = AddToNameHash(at, tentry.name, loc); if (code != PRSUCCESS) return code; nameOK:; } return PRSUCCESS; } static afs_int32 allocNextId(struct ubik_trans * at, struct prentry * cellEntry) { /* Id's for foreign cell entries are constructed as follows: * The 16 low order bits are the group id of the cell and the * top 16 bits identify the particular users in that cell */ afs_int32 id; afs_int32 cellid = ((ntohl(cellEntry->id)) & 0x0000ffff); id = (ntohl(cellEntry->nusers) + 1); while (FindByID(at, ((id << 16) | cellid))) { id++; if (id > 0xffff) return 0; } cellEntry->nusers = htonl(id); /* use the field nusers to keep * the next available id in that * foreign cell's group. Note : * It would seem more appropriate * to use ngroup for that and nusers * to enforce the quota, however pts * does not have an option to change * foreign users quota yet */ id = (id << 16) | cellid; return id; } static int inRange(struct prentry *cellEntry, afs_int32 aid) { afs_uint32 id, cellid, groupid; /* * The only thing that we want to make sure here is that * the id is in the legal range of this group. If it is * a duplicate we don't care since it will get caught * in a different check. */ cellid = aid & 0x0000ffff; groupid = (ntohl(cellEntry->id)) & 0x0000ffff; if (cellid != groupid) return 0; /* not in range */ /* * if we got here we're ok but we need to update the nusers * field in order to get the id correct the next time that * we try to allocate it automatically */ id = aid >> 16; if (id > ntohl(cellEntry->nusers)) cellEntry->nusers = htonl(id); return 1; } static int AddAuthGroup(struct prentry *tentry, prlist *alist, afs_int32 *size) { if (!(strchr(tentry->name, '@'))) return (AddToPRList(alist, size, AUTHUSERID)); else return PRSUCCESS; }