1 /* Copyright (C) 1990, 1989 Transarc Corporation - All rights reserved */
3 * P_R_P_Q_# (C) COPYRIGHT IBM CORPORATION 1988
4 * LICENSED MATERIALS - PROPERTY OF IBM
5 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
11 Information Technology Center
14 Modified May, 1989 by Jeff Schiller to keep disk file in
19 #include <afs/param.h>
21 #include <sys/types.h>
29 #include <netinet/in.h>
32 #include <afs/com_err.h>
38 extern struct ubik_dbase *dbase;
39 extern struct afsconf_dir *prdir;
42 static char *whoami = "ptserver";
44 /* CorrectUserName - Check to make sure a user name is OK. It must not include
45 * either a colon (or it would look like a group) or an atsign (or it would
46 * look like a foreign user). The length is checked as well to make sure
47 * that the user name, an atsign, and the local cell name will fit in
48 * PR_MAXNAMELEN. This is so this user can fit in another cells database as
49 * a foreign user with our cell name tacked on. This is a predicate, so it
50 * return one if name is OK and zero if name is bogus. */
52 static int CorrectUserName (name)
55 extern int pr_realmNameLen;
58 if (index (name, ':') || index(name, '\n')) return 0;
60 if (index (name, ':') || index(name, '@') || index(name, '\n')) return 0;
62 if (strlen (name) >= PR_MAXNAMELEN - pr_realmNameLen - 1) return 0;
66 /* CorrectGroupName - Like the above but handles more complicated cases caused
67 * by including the ownership in the name. The interface works by calculating
68 * the correct name based on a given name and owner. This allows easy use by
69 * rename, which then compares the correct name with the requested new name. */
71 static long CorrectGroupName (ut, aname, cid, oid, cname)
72 struct ubik_trans *ut;
73 char aname[PR_MAXNAMELEN]; /* name for group */
74 long cid; /* caller id */
75 long oid; /* owner of group */
76 char cname[PR_MAXNAMELEN]; /* correct name for group */
80 char *prefix; /* ptr to group owner part */
81 char *suffix; /* ptr to group name part */
82 char name[PR_MAXNAMELEN]; /* correct name for group */
83 struct prentry tentry;
85 if (strlen (aname) >= PR_MAXNAMELEN) return PRBADNAM;
86 admin = pr_noAuth || (cid==SYSADMINID) || IsAMemberOf(ut, cid, SYSADMINID);
88 if (oid == 0) oid = cid;
90 /* Determine the correct prefix for the name. */
91 if (oid == SYSADMINID) prefix = "system";
93 long loc = FindByID (ut, oid);
95 /* let admin create groups owned by non-existent ids (probably
96 * setting a group to own itself). Check that they look like
97 * groups (with a colon) or otherwise are good user names. */
99 strcpy (cname, aname);
104 code = pr_Read (ut, 0, loc, &tentry, sizeof(tentry));
105 if (code) return code;
106 if (ntohl(tentry.flags) & PRGRP) {
107 if ((tentry.count == 0) && !admin) return PRGROUPEMPTY;
108 /* terminate prefix at colon if there is one */
109 if (prefix = index(tentry.name, ':')) *prefix = 0;
111 prefix = tentry.name;
113 /* only sysadmin allow to use 'system:' prefix */
114 if ((strcmp (prefix, "system") == 0) && !admin) return PRPERM;
116 strcpy (name, aname); /* in case aname & cname are same */
117 suffix = index(name, ':');
119 /* sysadmin can make groups w/o ':', but they must still look like
120 * legal user names. */
121 if (!admin) return PRBADNAM;
122 strcpy (cname, name);
125 if (strlen(prefix)+strlen(suffix) >= PR_MAXNAMELEN) return PRBADNAM;
126 strcpy (cname, prefix);
127 strcat (cname, suffix);
130 /* check for legal name with either group rules or user rules */
131 if (suffix = index(cname, ':')) {
132 /* check for confusing characters */
134 if (index(cname, '\n') || /* restrict so recreate can work */
135 index(suffix+1, ':')) /* avoid multiple colons */
138 if (index(cname, '@') || /* avoid confusion w/ foreign users */
139 index(cname, '\n') || /* restrict so recreate can work */
140 index(suffix+1, ':')) /* avoid multiple colons */
144 if (!CorrectUserName (cname)) return PRBADNAM;
149 int AccessOK (ut, cid, tentry, mem, any)
150 struct ubik_trans *ut;
151 long cid; /* caller id */
152 struct prentry *tentry; /* object being accessed */
153 int mem; /* check membership in aid, if group */
154 int any; /* if set return true */
159 if (pr_noAuth) return 1;
160 if (cid == SYSADMINID) return 1; /* special case fileserver */
162 flags = tentry->flags;
166 flags = oid = aid = 0;
168 if (!(flags & PRACCESS)) /* provide default access */
170 flags |= PRP_GROUP_DEFAULT;
172 flags |= PRP_USER_DEFAULT;
174 if (flags & any) return 1;
177 IsAMemberOf (ut, cid, oid)) return 1;
179 if (aid > 0) { /* checking on a user */
180 if (aid == cid) return 1;
181 } else if (aid < 0) { /* checking on group */
182 if ((flags & mem) && IsAMemberOf (ut, cid, aid)) return 1;
184 if (IsAMemberOf (ut, cid, SYSADMINID)) return 1;
185 return 0; /* no access */
188 long CreateEntry (at, aname, aid, idflag, flag, oid, creator)
189 register struct ubik_trans *at;
190 char aname[PR_MAXNAMELEN];
197 /* get and init a new entry */
200 struct prentry tentry;
202 bzero(&tentry, sizeof(tentry));
204 if ((oid == 0) || (oid == ANONYMOUSID)) oid = creator;
207 code = CorrectGroupName (at, aname, creator, oid, tentry.name);
208 if (code) return code;
209 if (strcmp (aname, tentry.name) != 0) return PRBADNAM;
210 } else { /* non-group must not have colon */
211 if (!CorrectUserName(aname)) return PRBADNAM;
212 strcpy (tentry.name, aname);
215 if (FindByName(at,aname)) return PREXIST;
217 newEntry = AllocBlock(at);
218 if (!newEntry) return PRDBFAIL;
219 #ifdef PR_REMEMBER_TIMES
220 tentry.createTime = time(0);
223 tentry.flags |= PRGRP;
226 else if (flag & PRFOREIGN) {
227 tentry.flags |= PRFOREIGN;
230 else tentry.owner = SYSADMINID;
233 #define ADD_TO_AUTHUSER_GROUP 1
234 #define AUTHUSER_GROUP "system:authuser"
238 if (!(atsign= index(aname,'@'))) { /* No @ so local cell*/
242 code= AllocID(at,flag,&tentry.id);
243 if (code != PRSUCCESS) return code;
246 /*foreign cells are represented by the group system:authuser@cell*/
248 /* it's a new foreign cell so the format
249 * must be AUTHUSER_GROUP@cellname */
253 badFormat = strcmp(AUTHUSER_GROUP, aname);
255 if (badFormat) return PRBADNAM;
259 code= AllocID(at,flag,&tentry.id);
260 if (code != PRSUCCESS) return code;
263 /* it's a foreign cell entry */
266 struct prentry centry;
267 extern long allocNextId();
268 extern long AddToEntry();
270 cellGroup = (char *) malloc (strlen(AUTHUSER_GROUP) +
272 strcpy(cellGroup, AUTHUSER_GROUP);
273 strcat(cellGroup, atsign);
274 pos = FindByName(at,cellGroup);
276 /* if the group doesn't exist don't allow user creation */
277 if (!pos) return PRBADNAM;
279 code = pr_Read (at, 0, pos, ¢ry, sizeof(centry));
280 if (code) return code;
281 tentry.cellid = ntohl(centry.id);
282 /* cellid is the id of the group representing the cell */
285 if (!inRange(¢ry,*aid))
286 return PRBADARG; /* the id specified is not in
287 * the id space of the group */
290 /* allocNextID() will allocate the next id
291 * in that cell's space */
292 tentry.id = allocNextId(¢ry);
294 /* charge the cell group for the new user and test quota */
295 if (!(ntohl(centry.flags) & PRQUOTA)) {
296 /* quota uninitialized, so initialize it now */
297 centry.flags = htonl (ntohl(centry.flags) | PRQUOTA);
298 centry.ngroups = htonl(30);
301 centry.ngroups = htonl(ntohl(centry.ngroups) - 1);
302 if (ntohl(centry.ngroups) < 0)
303 if (!pr_noAuth) return PRNOMORE;
305 #if !ADD_TO_AUTHUSER_GROUP
306 centry.count = htonl(ntohl(centry.ngroups) +1);
307 /* keep count of how many people are in the group. */
310 code = pr_Write (at, 0, pos, ¢ry, sizeof(centry));
311 /* write updated entry for group */
313 /* Now add the new user entry to the database */
315 tentry.creator = creator;
317 code = pr_WriteEntry(at, 0, newEntry, &tentry);
318 if (code) return PRDBFAIL;
319 code = AddToIDHash(at,*aid,newEntry);
320 if (code != PRSUCCESS) return code;
321 code = AddToNameHash(at,aname,newEntry);
322 if (code != PRSUCCESS) return code;
323 if (inc_header_word (at, foreigncount, 1)) return PRDBFAIL;
325 #if ADD_TO_AUTHUSER_GROUP
327 /* Now add the entry to the authuser group for this cell.
328 * We will reread the entries for the user and the group
329 * instead of modifying them before writing them in the
330 * previous steps. Although not very efficient, much simpler */
332 /* First update the group entry */
333 pos = FindByID(at,tentry.cellid);
334 if (!pos) return PRBADNAM;
335 code = pr_ReadEntry (at, 0, pos, ¢ry);
336 if (code) return code;
337 code = AddToEntry(at, ¢ry,pos,*aid);
338 if (code) return code;
339 /* and now the user entry */
340 pos = FindByID(at,*aid);
341 if (!pos) return PRBADNAM;
342 code = pr_ReadEntry(at, 0, pos, &tentry);
343 if (code) return code;
344 code = AddToEntry(at, &tentry,pos,tentry.cellid);
345 if (code) return code;
355 #else /* !CROSS_CELL */
360 code= AllocID(at,flag,&tentry.id);
361 if (code != PRSUCCESS) return code;
363 #endif /* !CROSS_CELL */
366 /* group ids are negative */
367 if (tentry.id < (long)ntohl(cheader.maxGroup)) {
368 code = set_header_word (at, maxGroup, htonl(tentry.id));
369 if (code) return PRDBFAIL;
372 else if (flag & PRFOREIGN) {
373 if (tentry.id > (long)ntohl(cheader.maxForeign)) {
374 code = set_header_word (at, maxForeign, htonl(tentry.id));
375 if (code) return PRDBFAIL;
379 if (tentry.id > (long)ntohl(cheader.maxID)) {
380 code = set_header_word (at, maxID, htonl(tentry.id));
381 if (code) return PRDBFAIL;
384 /* PRACCESS is off until set, defaults provided in AccessOK */
385 if (flag == 0) { /* only normal users get quota */
386 tentry.flags |= PRQUOTA;
387 tentry.ngroups = tentry.nusers = 20;
390 if (flag & (PRGRP | PRFOREIGN)) {
391 long loc = FindByID (at, creator);
392 struct prentry centry;
393 long *nP; /* ptr to entry to be decremented */
394 long n; /* quota to check */
396 if (loc) { /* this should only fail during initialization */
397 code = pr_Read (at, 0, loc, ¢ry, sizeof(centry));
398 if (code) return code;
400 if (flag & PRGRP) nP = ¢ry.ngroups;
401 else if (flag & PRFOREIGN) nP = ¢ry.nusers;
405 if (!(ntohl(centry.flags) & PRQUOTA)) {
406 /* quota uninitialized, so do it now */
407 centry.flags = htonl (ntohl(centry.flags) | PRQUOTA);
408 centry.ngroups = centry.nusers = htonl(20);
413 !IsAMemberOf (at, creator, SYSADMINID))
416 else { /* don't use up admin user's quota */
417 int admin = ((creator == SYSADMINID) ||
418 IsAMemberOf (at, creator, SYSADMINID));
419 if (!admin) *nP = htonl(n-1);
422 code = pr_Write (at, 0, loc, ¢ry, sizeof(centry));
423 if (code) return code;
425 } /* need to check creation quota */
426 tentry.creator = creator;
428 code = pr_WriteEntry(at, 0, newEntry, &tentry);
429 if (code) return PRDBFAIL;
430 code = AddToIDHash(at,*aid,newEntry);
431 if (code != PRSUCCESS) return code;
432 code = AddToNameHash(at,aname,newEntry);
433 if (code != PRSUCCESS) return code;
434 if (tentry.flags & PRGRP) {
435 code = AddToOwnerChain(at,tentry.id,oid);
436 if (code) return code;
438 if (tentry.flags & PRGRP) {
439 if (inc_header_word (at, groupcount, 1)) return PRDBFAIL;
441 else if (tentry.flags & PRFOREIGN) {
442 if (inc_header_word (at, foreigncount, 1)) return PRDBFAIL;
444 else if (tentry.flags & PRINST) {
445 if (inc_header_word (at, instcount, 1)) return PRDBFAIL;
448 if (inc_header_word (at, usercount, 1)) return PRDBFAIL;
454 /* RemoveFromEntry - remove aid from bid's entries list, freeing a continuation
455 * entry if appropriate */
457 long RemoveFromEntry (at, aid, bid)
458 register struct ubik_trans *at;
463 struct prentry tentry;
464 struct contentry centry;
465 struct contentry hentry;
471 if (aid == bid) return PRINCONSISTENT;
472 bzero(&hentry,sizeof(hentry));
473 temp = FindByID(at,bid);
474 if (temp == 0) return PRNOENT;
475 code = pr_ReadEntry(at, 0, temp, &tentry);
476 if (code != 0) return code;
477 #ifdef PR_REMEMBER_TIMES
478 tentry.removeTime = time(0);
480 for (i=0;i<PRSIZE;i++) {
481 if (tentry.entries[i] == aid) {
482 tentry.entries[i] = PRBADID;
484 code = pr_WriteEntry(at,0,temp,&tentry);
485 if (code != 0) return code;
488 if (tentry.entries[i] == 0) /* found end of list */
493 while (nptr != NULL) {
494 code = pr_ReadCoEntry(at,0,nptr,¢ry);
495 if (code != 0) return code;
496 if ((centry.id != bid) || !(centry.flags & PRCONT)) return PRDBBAD;
497 for (i=0;i<COSIZE;i++) {
498 if (centry.entries[i] == aid) {
499 centry.entries[i] = PRBADID;
500 for (j=0;j<COSIZE;j++)
501 if (centry.entries[j] != PRBADID &&
502 centry.entries[j] != 0) break;
503 if (j == COSIZE) { /* can free this block */
505 tentry.next = centry.next;
508 hentry.next = centry.next;
509 code = pr_WriteCoEntry (at, 0, hloc, &hentry);
510 if (code != 0) return code;
512 code = FreeBlock (at, nptr);
513 if (code) return code;
515 else { /* can't free it yet */
516 code = pr_WriteCoEntry(at,0,nptr,¢ry);
517 if (code != 0) return code;
520 code = pr_WriteEntry(at,0,temp,&tentry);
521 if (code) return PRDBFAIL;
524 if (centry.entries[i] == 0) return PRNOENT;
525 } /* for all coentry slots */
528 bcopy(¢ry,&hentry,sizeof(centry));
529 } /* while there are coentries */
533 /* DeleteEntry - delete the entry in tentry at loc, removing it from all
534 * groups, putting groups owned by it on orphan chain, and freeing the space */
536 long DeleteEntry (at, tentry, loc)
537 register struct ubik_trans *at;
538 struct prentry *tentry;
542 struct contentry centry;
547 if (index(tentry->name,'@')) {
548 if (tentry->flags & PRGRP) {
549 /* If there are still foreign user accounts from that cell
550 * don't delete the group */
551 if (tentry->count) return PRBADARG;
553 /* It's a user adjust the group quota upwards */
554 long loc = FindByID (at, tentry->cellid);
555 struct prentry centry;
557 code = pr_Read (at, 0, loc, ¢ry, sizeof(centry));
558 if (code) return code;
559 if (ntohl(centry.flags) & PRQUOTA) {
560 centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
562 #if !ADD_TO_AUTHUSER_GROUP
563 /* if this is a foreign cell entry then decrement the number of
564 * existing users in the prentry of the authuser group for that
567 centry.count = htonl(ntohl(centry.count) - 1);
569 code = pr_Write (at, 0, loc, ¢ry, sizeof(centry));
570 if (code) return code;
574 #endif /* CROSS_CELL */
576 /* First remove the entire membership list */
577 for (i=0;i<PRSIZE;i++) {
578 if (tentry->entries[i] == PRBADID) continue;
579 if (tentry->entries[i] == 0) break;
580 code = RemoveFromEntry (at, tentry->id, tentry->entries[i]);
581 if (code) return code;
584 while (nptr != NULL) {
585 code = pr_ReadCoEntry(at,0,nptr,¢ry);
586 if (code != 0) return PRDBFAIL;
587 for (i=0;i<COSIZE;i++) {
588 if (centry.entries[i] == PRBADID) continue;
589 if (centry.entries[i] == 0) break;
590 code = RemoveFromEntry (at, tentry->id, centry.entries[i]);
591 if (code) return code;
593 code = FreeBlock (at, nptr); /* free continuation block */
594 if (code) return code;
598 /* Remove us from other's owned chain. Note that this will zero our owned
599 * field (on disk) so this step must follow the above step in case we are
600 * on our own owned list. */
601 if (tentry->flags & PRGRP) {
603 code = RemoveFromOwnerChain (at, tentry->id, tentry->owner);
604 if (code) return code;
607 code = RemoveFromOrphan (at, tentry->id);
608 if (code) return code;
612 code = RemoveFromIDHash(at,tentry->id,&loc);
613 if (code != PRSUCCESS) return code;
614 code = RemoveFromNameHash(at,tentry->name,&loc);
615 if (code != PRSUCCESS) return code;
617 if (tentry->flags & (PRGRP | PRFOREIGN)) {
618 long loc = FindByID (at, tentry->creator);
619 struct prentry centry;
622 code = pr_Read (at, 0, loc, ¢ry, sizeof(centry));
623 if (code) return code;
624 admin = ((tentry->creator == SYSADMINID) ||
625 IsAMemberOf (at, tentry->creator, SYSADMINID));
626 if (ntohl(centry.flags) & PRQUOTA) {
627 if ((tentry->flags & PRGRP) &&
628 !(admin && (ntohl(centry.ngroups) >= 20))) {
629 centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
630 } else if ((tentry->flags & PRFOREIGN) &&
631 !(admin && (ntohl(centry.nusers) >= 20))) {
632 centry.nusers = htonl(ntohl(centry.nusers) + 1);
635 code = pr_Write (at, 0, loc, ¢ry, sizeof(centry));
636 if (code) return code;
640 if (tentry->flags & PRGRP) {
641 if (inc_header_word (at, groupcount, -1)) return PRDBFAIL;
643 else if (tentry->flags & PRFOREIGN) {
644 if (inc_header_word (at, foreigncount, -1)) return PRDBFAIL;
646 else if (tentry->flags & PRINST) {
647 if (inc_header_word (at, instcount, -1)) return PRDBFAIL;
650 if (inc_header_word (at, usercount, -1)) return PRDBFAIL;
652 code = FreeBlock(at, loc);
656 /* AddToEntry - add aid to entry's entries list, alloc'ing a continuation block
659 * Note the entry is written out by this routine. */
661 long AddToEntry (tt, entry, loc, aid)
662 struct ubik_trans *tt;
663 struct prentry *entry;
669 struct contentry nentry;
670 struct contentry aentry;
672 long last; /* addr of last cont. block */
677 if (entry->id == aid) return PRINCONSISTENT;
678 #ifdef PR_REMEMBER_TIMES
679 entry->addTime = time(0);
681 for (i=0;i<PRSIZE;i++) {
682 if (entry->entries[i] == aid)
684 if (entry->entries[i] == PRBADID) { /* remember this spot */
688 else if (entry->entries[i] == 0) { /* end of the line */
698 while (nptr != NULL) {
699 code = pr_ReadCoEntry(tt,0,nptr,&nentry);
700 if (code != 0) return code;
702 if (!(nentry.flags & PRCONT)) return PRDBFAIL;
703 for (i=0;i<COSIZE;i++) {
704 if (nentry.entries[i] == aid)
706 if (nentry.entries[i] == PRBADID) {
712 else if (nentry.entries[i] == 0) {
722 if (slot != -1) { /* we found a place */
724 if (first) { /* place is in first block */
725 entry->entries[slot] = aid;
726 code = pr_WriteEntry (tt, 0, loc, entry);
727 if (code != 0) return code;
730 code = pr_WriteEntry (tt, 0, loc, entry);
731 if (code) return code;
732 code = pr_ReadCoEntry(tt,0,cloc,&aentry);
733 if (code != 0) return code;
734 aentry.entries[slot] = aid;
735 code = pr_WriteCoEntry(tt,0,cloc,&aentry);
736 if (code != 0) return code;
739 /* have to allocate a continuation block if we got here */
740 nptr = AllocBlock(tt);
742 /* then we should tack new block after last block in cont. chain */
744 code = pr_WriteCoEntry(tt,0,last,&nentry);
745 if (code != 0) return code;
750 bzero(&aentry,sizeof(aentry));
751 aentry.flags |= PRCONT;
752 aentry.id = entry->id;
754 aentry.entries[0] = aid;
755 code = pr_WriteCoEntry(tt,0,nptr,&aentry);
756 if (code != 0) return code;
757 /* don't forget to update count, here! */
759 code = pr_WriteEntry (tt, 0, loc, entry);
764 long AddToPRList (alist, sizeP, id)
769 if (alist->prlist_len >= PR_MAXGROUPS) return PRTOOMANY;
770 if (alist->prlist_len >= *sizeP) {
771 *sizeP = *sizeP + 100;
772 if (*sizeP > PR_MAXGROUPS) *sizeP = PR_MAXGROUPS;
774 (long *) ((alist->prlist_val) ?
775 realloc (alist->prlist_val, (*sizeP)*sizeof(long)) :
776 malloc ((*sizeP)*sizeof(long)));
778 alist->prlist_val[alist->prlist_len++] = id;
782 long GetList (at, tentry, alist, add)
783 struct ubik_trans *at;
784 struct prentry *tentry;
790 struct contentry centry;
797 alist->prlist_val = 0;
798 alist->prlist_len = 0;
800 for (i=0;i<PRSIZE;i++) {
801 if (tentry->entries[i] == PRBADID) continue;
802 if (tentry->entries[i] == 0) break;
803 code = AddToPRList (alist, &size, tentry->entries[i]);
804 if (code) return code;
808 while (nptr != NULL) {
809 /* look through cont entries */
810 code = pr_ReadCoEntry(at,0,nptr,¢ry);
811 if (code != 0) return code;
812 for (i=0;i<COSIZE;i++) {
813 if (centry.entries[i] == PRBADID) continue;
814 if (centry.entries[i] == 0) break;
815 code = AddToPRList (alist, &size, centry.entries[i]);
816 if (code) return code;
819 if (count++ > 50) IOMGR_Poll(), count = 0;
822 if (add) { /* this is for a CPS, so tack on appropriate stuff */
823 if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) {
825 if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
826 (code = AddAuthGroup(tentry, alist, &size)) ||
827 (code = AddToPRList (alist, &size, tentry->id))) return code;
829 if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
830 (code = AddToPRList (alist, &size, AUTHUSERID)) ||
831 (code = AddToPRList (alist, &size, tentry->id))) return code;
835 if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
836 (code = AddToPRList (alist, &size, tentry->id))) return code;
839 if (alist->prlist_len > 100) IOMGR_Poll();
840 qsort(alist->prlist_val,alist->prlist_len,sizeof(long),IDCmp);
844 long GetOwnedChain (ut, next, alist)
845 struct ubik_trans *ut;
848 { register long code;
849 struct prentry tentry;
855 alist->prlist_val = 0;
856 alist->prlist_len = 0;
859 code = pr_Read (ut, 0, next, &tentry, sizeof(tentry));
860 if (code) return code;
861 code = AddToPRList (alist, &size, ntohl(tentry.id));
862 if (code) return code;
863 next = ntohl(tentry.nextOwned);
864 if (count++ > 50) IOMGR_Poll(), count = 0;
866 if (alist->prlist_len > 100) IOMGR_Poll();
867 qsort(alist->prlist_val,alist->prlist_len,sizeof(long),IDCmp);
871 long GetMax(at,uid,gid)
872 register struct ubik_trans *at;
876 *uid = ntohl(cheader.maxID);
877 *gid = ntohl(cheader.maxGroup);
881 long SetMax(at,id,flag)
882 register struct ubik_trans *at;
888 cheader.maxGroup = htonl(id);
889 code = pr_Write(at,0,16,(char *)&cheader.maxGroup,sizeof(cheader.maxGroup));
890 if (code != 0) return code;
893 cheader.maxID = htonl(id);
894 code = pr_Write(at,0,20,(char *)&cheader.maxID,sizeof(cheader.maxID));
895 if (code != 0) return code;
905 struct ubik_trans *tt;
909 static struct ubik_version curver;
910 struct ubik_version newver;
913 /* init the database. We'll try reading it, but if we're starting from scratch, we'll have to do a write transaction. */
915 pr_noAuth = afsconf_GetNoAuthFlag(prdir);
917 code = ubik_BeginTransReadAny(dbase,UBIK_READTRANS, &tt);
918 if (code) return code;
919 code = ubik_SetLock(tt,1,1,LOCKREAD);
927 bzero(&curver,sizeof(curver));
929 } else if (!ubik_CacheUpdate (tt)) {
930 code = ubik_EndTrans(tt);
934 code = ubik_GetVersion(tt,&newver);
935 if (vcmp(curver,newver) == 0) {
937 code = ubik_EndTrans(tt);
938 if (code) return code;
941 bcopy(&newver,&curver,sizeof(struct ubik_version));
944 len = sizeof(cheader);
945 code = pr_Read(tt, 0, 0, (char *) &cheader, len);
947 com_err (whoami, code, "couldn't read header");
951 if ((ntohl(cheader.version) == PRDBVERSION) &&
952 ntohl(cheader.headerSize) == sizeof(cheader) &&
953 ntohl(cheader.eofPtr) != NULL &&
954 FindByID(tt,ANONYMOUSID) != 0){
955 /* database exists, so we don't have to build it */
956 code = ubik_EndTrans(tt);
957 if (code) return code;
960 /* else we need to build a database */
961 code = ubik_EndTrans(tt);
962 if (code) return code;
964 /* Only rebuild database if the db was deleted (the header is zero) and we
965 are running noAuth. */
966 { char *bp = (char *)&cheader;
968 for (i=0; i<sizeof(cheader); i++)
971 com_err (whoami, code,
972 "Can't rebuild database because it is not empty");
978 com_err (whoami, code,
979 "Can't rebuild database because not running NoAuth");
983 code = ubik_BeginTrans(dbase,UBIK_WRITETRANS, &tt);
984 if (code) return code;
986 code = ubik_SetLock(tt,1,1,LOCKWRITE);
992 /* before doing a rebuild, check again that the dbase looks bad, because
993 * the previous check was only under a ReadAny transaction, and there could
994 * actually have been a good database out there. Now that we have a
995 * real write transaction, make sure things are still bad.
997 if ((ntohl(cheader.version) == PRDBVERSION) &&
998 ntohl(cheader.headerSize) == sizeof(cheader) &&
999 ntohl(cheader.eofPtr) != NULL &&
1000 FindByID(tt,ANONYMOUSID) != 0){
1001 /* database exists, so we don't have to build it */
1002 code = ubik_EndTrans(tt);
1003 if (code) return code;
1007 /* Initialize the database header */
1008 if ((code = set_header_word (tt, version, htonl(PRDBVERSION))) ||
1009 (code = set_header_word (tt, headerSize, htonl(sizeof(cheader)))) ||
1010 (code = set_header_word (tt, eofPtr, cheader.headerSize))) {
1011 com_err (whoami, code, "couldn't write header words");
1012 ubik_AbortTrans(tt);
1016 #define InitialGroup(id,name) do { \
1018 long flag = (id) < 0 ? PRGRP : 0; \
1019 code = CreateEntry \
1020 (tt, (name), &temp, /*idflag*/1, flag, SYSADMINID, SYSADMINID); \
1022 com_err (whoami, code, "couldn't create %s with id %di.", \
1024 ubik_AbortTrans(tt); \
1029 InitialGroup (SYSADMINID, "system:administrators");
1030 InitialGroup (ANYUSERID, "system:anyuser");
1031 InitialGroup (AUTHUSERID, "system:authuser");
1032 InitialGroup (ANONYMOUSID, "anonymous");
1034 /* Well, we don't really want the max id set to anonymousid, so we'll set
1036 code = set_header_word (tt, maxID, 0); /* correct in any byte order */
1038 com_err (whoami, code, "couldn't reset max id");
1039 ubik_AbortTrans(tt);
1043 code = ubik_EndTrans(tt);
1044 if (code) return code;
1053 * This function follows the "owned" and "nextOwned" chains and verifies
1054 * that the data is consistent. The chain is only traversed as far as is
1055 * necessary for efficiency reasons:
1056 * o Follow the "nextOwned" chain only if we had to adjust ourself
1057 * (either renaming ourself or adjusting the owner id)
1058 * o Follow the "owned" chain if we had to adjust ourself (see above), or
1059 * we are the parent that started this mess (tentry->id == oldid/newid)
1062 long fixOwnerChain(at,loc,tentry,oldid,newid)
1063 struct ubik_trans *at;
1064 struct prentry *tentry;
1065 long loc, oldid, newid;
1067 char name[PR_MAXNAMELEN];
1068 struct prentry nentry;
1073 if (newid && oldid != newid) {
1074 if (tentry->owner == oldid) tentry->owner = newid;
1075 if (tentry->creator == oldid) tentry->creator = newid;
1077 /* If our owner has changed, continue along the nextOwned chain */
1078 if (tentry->owner == newid) nextOwn=1;
1081 strcpy(name, tentry->name);
1082 code=CorrectGroupName(at,name,tentry->creator,tentry->owner,tentry->name);
1083 if (code) return code;
1084 if (strcmp(name, tentry->name)) {
1086 /* If we had to rename ourself, there are probably others
1087 * along the nextOwned chain that have to do the same. */
1090 pos = FindByName(at,tentry->name);
1091 if (pos) return PREXIST;
1093 code = RemoveFromNameHash (at, name, &loc);
1094 if (code) return code;
1095 code = AddToNameHash(at,tentry->name,loc);
1096 if (code) return code;
1099 code = pr_WriteEntry(at, 0, loc, tentry);
1100 if (code) return code;
1102 if ((loc = tentry->owned) &&
1103 (nextOwn || (newid && tentry->id == newid) || (tentry->id == oldid))) {
1105 code = pr_ReadEntry(at,0,loc,&nentry);
1106 if (code) return code;
1107 code = fixOwnerChain(at, loc, &nentry, oldid, newid);
1108 if (code) return code;
1111 if (nextOwn && (loc = tentry->nextOwned)) {
1113 code = pr_ReadEntry(at,0,loc,&nentry);
1114 if (code) return code;
1115 code = fixOwnerChain(at, loc, &nentry, oldid, newid);
1116 if (code) return code;
1121 long ChangeEntry (at, aid, cid, name, oid, newid)
1122 struct ubik_trans *at;
1129 register long code, nptr, i;
1131 struct prentry tentry, gentry;
1132 struct contentry centry;
1135 char holder[PR_MAXNAMELEN];
1136 char temp[PR_MAXNAMELEN];
1137 char oldname[PR_MAXNAMELEN];
1142 bzero(holder,PR_MAXNAMELEN);
1143 bzero(temp,PR_MAXNAMELEN);
1144 loc = FindByID(at,aid);
1145 if (!loc) return PRNOENT;
1146 code = pr_ReadEntry(at,0,loc,&tentry);
1147 if (code) return PRDBFAIL;
1148 if (tentry.owner != cid &&
1149 !IsAMemberOf(at,cid,SYSADMINID) &&
1150 !IsAMemberOf(at,cid,tentry.owner) &&
1151 !pr_noAuth) return PRPERM;
1152 #ifdef PR_REMEMBER_TIMES
1153 tentry.changeTime = time(0);
1156 /* we're actually trying to change the id */
1157 if (aid != newid && newid != 0) {
1158 if (!IsAMemberOf(at,cid,SYSADMINID) && !pr_noAuth) return PRPERM;
1159 pos = FindByID(at,newid);
1160 if (pos) return PRIDEXIST; /* new id already in use! */
1161 if ((aid < 0 && newid > 0) || (aid > 0 && newid < 0)) return PRPERM;
1162 /* if new id is not in use, rehash things */
1163 code = RemoveFromIDHash(at,aid,&loc);
1164 if (code != PRSUCCESS) return code;
1166 code = pr_WriteEntry(at,0,loc,&tentry);
1167 if (code) return code;
1168 code = AddToIDHash(at,tentry.id,loc);
1169 if (code) return code;
1170 /* get current data */
1171 code = pr_ReadEntry(at,0,loc,&tentry);
1172 if (code) return PRDBFAIL;
1174 for (i=0;i<PRSIZE;i++) {
1175 if (tentry.entries[i] == PRBADID) continue;
1176 if (tentry.entries[i] == 0) break;
1177 if (pos = FindByID(at, tentry.entries[i])) {
1178 code = pr_ReadEntry(at,0,pos,&gentry);
1179 if (code) return PRDBFAIL;
1180 code = AddToEntry(at,&gentry,pos,newid);
1181 if (code) return PRDBFAIL;
1182 code = RemoveFromEntry(at,aid,tentry.entries[i]);
1183 if (code) return PRDBFAIL;
1185 fprintf(stderr, "ChangeEntry: %d: Couldn't locate group %d\n",
1186 newid, tentry.entries[i]);
1192 /* look through cont entries */
1193 code = pr_ReadCoEntry(at,0,nptr,¢ry);
1194 if (code != 0) return code;
1195 for (i=0;i<COSIZE;i++) {
1196 if (centry.entries[i] == PRBADID) continue;
1197 if (centry.entries[i] == 0) break;
1198 if (pos = FindByID(at, centry.entries[i])) {
1199 code = pr_ReadEntry(at,0,pos,&gentry);
1200 if (code) return PRDBFAIL;
1201 code = AddToEntry(at,&gentry,pos,newid);
1202 if (code) return PRDBFAIL;
1203 code = RemoveFromEntry(at,aid,centry.entries[i]);
1204 if (code) return PRDBFAIL;
1213 atsign = index(tentry.name, '@'); /* check for foreign entry */
1216 /* Change the owner */
1217 if (tentry.owner != oid && oid) {
1218 /* only groups can have their owner's changed */
1219 if (!(tentry.flags & PRGRP)) return PRPERM;
1221 /* don't allow modifications to foreign cell group owners */
1222 if (atsign) return PRPERM;
1224 oldowner = tentry.owner;
1226 /* The entry must be written through first so Remove and Add routines
1227 * can operate on disk data */
1228 code = pr_WriteEntry(at,0,loc,(char *)&tentry);
1229 if (code) return PRDBFAIL;
1230 if (tentry.flags & PRGRP) {
1231 /* switch owner chains */
1232 if (oldowner) /* if it has an owner */
1233 code = RemoveFromOwnerChain(at,tentry.id,oldowner);
1234 else /* must be an orphan */
1235 code = RemoveFromOrphan(at,tentry.id);
1236 if (code) return code;
1237 code = AddToOwnerChain(at,tentry.id,tentry.owner);
1238 if (code) return code;
1240 /* fix up the name */
1241 if ((tentry.flags & PRGRP) && (strlen(name) == 0)) name = tentry.name;
1242 /* get current data */
1243 code = pr_ReadEntry(at,0,loc,&tentry);
1244 if (code) return PRDBFAIL;
1247 /* Change the name, if name is a ptr to tentry.name then this name change
1248 * is due to a chown, otherwise caller has specified a new name */
1249 if ((name == tentry.name) ||
1250 (*name && (strcmp (tentry.name, name) != 0))) {
1251 strncpy (oldname, tentry.name, PR_MAXNAMELEN);
1252 if (tentry.flags & PRGRP) {
1254 if (atsign) return PRPERM;
1256 code = CorrectGroupName (at, name, cid, tentry.owner, tentry.name);
1257 if (code) return code;
1259 if (name == tentry.name) { /* owner fixup */
1260 if (strcmp (oldname, tentry.name) == 0) goto nameOK;
1261 } else { /* new name, caller must be correct */
1262 if (strcmp (name, tentry.name) != 0) return PRBADNAM;
1266 /* Allow a foreign name change only if the cellname part is
1270 newatsign = index (name, '@');
1271 if (newatsign != atsign){ /* if they are the same no problem*/
1272 /* if the pointers are not equal the strings better be */
1273 if ((atsign == 0) || (newatsign == 0) ||
1274 strcmp (atsign,newatsign)) return PRPERM;
1277 if (!CorrectUserName(name)) return PRBADNAM;
1280 pos = FindByName(at,name);
1281 if (pos) return PREXIST;
1282 code = RemoveFromNameHash (at, oldname, &loc);
1283 if (code != PRSUCCESS) return code;
1284 strncpy (tentry.name, name, PR_MAXNAMELEN);
1285 code = pr_WriteEntry(at,0,loc,(char *)&tentry);
1286 if (code) return PRDBFAIL;
1287 code = AddToNameHash(at,tentry.name,loc);
1288 if (code != PRSUCCESS) return code;
1291 return (fixOwnerChain(at, loc, &tentry, aid, newid));
1295 long allocNextId(cellEntry)
1296 struct prentry *cellEntry;
1298 /* Id's for foreign cell entries are constructed as follows:
1299 * The 16 low order bits are the group id of the cell and the
1300 * top 16 bits identify the particular users in that cell */
1304 id = (ntohl(cellEntry -> nusers) +1);
1305 cellEntry -> nusers = htonl(id);
1306 /* use the field nusers to keep the last used id in that
1307 * foreign cell's group.
1309 * Note: It would seem more appropriate to use ngroup for
1310 * that and nusers to enforce the quota, however pts does not
1311 * have an option to change foreign users quota yet. */
1312 id = (id << 16) | ((ntohl(cellEntry-> id)) & 0x0000ffff);
1316 int inRange(cellEntry,aid)
1317 struct prentry *cellEntry;
1320 unsigned long id,cellid,groupid;
1322 /* The only thing that we want to make sure here is that the id
1323 * is in the legal range of this group. If it is a duplicate we
1324 * don't care since it will get in a different check. */
1325 cellid = aid & 0x0000ffff;
1326 groupid = (ntohl(cellEntry-> id)) & 0x0000ffff;
1327 if (cellid != groupid) return 0; /* not in range */
1329 /* if we got here we're ok but we need to update the nusers field
1330 * in order to get the id correct the next time that we try to
1331 * allocate it automatically. */
1333 if (id > ntohl(cellEntry -> nusers))
1334 cellEntry -> nusers = htonl(id);
1338 AddAuthGroup(tentry, alist, size)
1339 struct prentry *tentry;
1343 if (!(index(tentry->name, '@')))
1344 return (AddToPRList (alist, size, AUTHUSERID));
1345 #if ADD_TO_AUTHUSER_GROUP
1348 return (AddToPRList (alist, size, tentry->cellid));
1351 #endif /* CROSS_CELL */