pts-sysviewer-should-be-able-to-listowned-20020521
[openafs.git] / src / ptserver / ptutils.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID("$Header$");
14
15 #include <afs/stds.h>
16 #include <sys/types.h>
17 #include <stdio.h>
18 #ifdef AFS_NT40_ENV 
19 #include <winsock2.h>
20 #else
21 #include <netinet/in.h>
22 #endif
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #else
26 #ifdef HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29 #endif
30 #include <lock.h>
31 #include <ubik.h>
32 #include <rx/xdr.h>
33 #include <afs/com_err.h>
34 #include <afs/cellconfig.h>
35 #include "ptserver.h"
36 #include "pterror.h"
37 #include <stdlib.h>
38
39 /* Foreign cells are represented by the group system:authuser@cell*/
40 #define AUTHUSER_GROUP "system:authuser"
41
42
43 extern struct ubik_dbase *dbase;
44 extern struct afsconf_dir *prdir;
45 extern int pr_noAuth;
46 extern int IDCmp();
47
48 extern afs_int32 AddToEntry();
49 static char *whoami = "ptserver";
50
51 /* CorrectUserName - Check to make sure a user name is OK.  It must not include
52  *   either a colon (or it would look like a group) or an atsign (or it would
53  *   look like a foreign user).  The length is checked as well to make sure
54  *   that the user name, an atsign, and the local cell name will fit in
55  *   PR_MAXNAMELEN.  This is so this user can fit in another cells database as
56  *   a foreign user with our cell name tacked on.  This is a predicate, so it
57  *   return one if name is OK and zero if name is bogus. */
58
59 static int CorrectUserName (name)
60   char *name;
61 {
62     extern int pr_realmNameLen;
63
64     /* We accept foreign names, so we will deal with '@' later */
65     if (strchr (name, ':') || strchr(name, '\n')) return 0;
66     if (strlen (name) >= PR_MAXNAMELEN - pr_realmNameLen - 1) return 0; 
67     return 1;
68 }
69
70 /* CorrectGroupName - Like the above but handles more complicated cases caused
71  * by including the ownership in the name.  The interface works by calculating
72  * the correct name based on a given name and owner.  This allows easy use by
73  * rename, which then compares the correct name with the requested new name. */
74
75 static afs_int32 CorrectGroupName (ut, aname, cid, oid, cname)
76   struct ubik_trans *ut;
77   char aname[PR_MAXNAMELEN];            /* name for group */
78   afs_int32 cid;                                /* caller id */
79   afs_int32 oid;                                /* owner of group */
80   char cname[PR_MAXNAMELEN];            /* correct name for group */
81 {
82     afs_int32  code;
83     int   admin;
84     char *prefix;                       /* ptr to group owner part */
85     char *suffix;                       /* ptr to group name part */
86     char  name[PR_MAXNAMELEN];          /* correct name for group */
87     struct prentry tentry;
88
89     if (strlen (aname) >= PR_MAXNAMELEN) return PRBADNAM;
90     admin = pr_noAuth || IsAMemberOf (ut, cid, SYSADMINID);
91
92     if (oid == 0) oid = cid;
93
94     /* Determine the correct prefix for the name. */
95     if (oid == SYSADMINID) prefix = "system";
96     else {
97         afs_int32 loc = FindByID (ut, oid);
98         if (loc == 0) {
99             /* let admin create groups owned by non-existent ids (probably
100              * setting a group to own itself).  Check that they look like
101              * groups (with a colon) or otherwise are good user names. */
102             if (admin) {
103                 strcpy (cname, aname);
104                 goto done;
105             }
106             return PRNOENT;
107         }
108         code = pr_Read (ut, 0, loc, &tentry, sizeof(tentry));
109         if (code) return code;
110         if (ntohl(tentry.flags) & PRGRP) {
111             if ((tentry.count == 0) && !admin) return PRGROUPEMPTY;
112             /* terminate prefix at colon if there is one */
113             if ((prefix = strchr(tentry.name, ':'))) *prefix = 0;
114         }
115         prefix = tentry.name;
116     }
117     /* only sysadmin allow to use 'system:' prefix */
118     if ((strcmp (prefix, "system") == 0) && !admin) return PRPERM;
119
120     strcpy (name, aname);               /* in case aname & cname are same */
121     suffix = strchr(name, ':');
122     if (suffix == 0) {
123         /* sysadmin can make groups w/o ':', but they must still look like
124          * legal user names. */
125         if (!admin) return PRBADNAM;
126         strcpy (cname, name);
127     }
128     else {
129         if (strlen(prefix)+strlen(suffix) >= PR_MAXNAMELEN) return PRBADNAM;
130         strcpy (cname, prefix);
131         strcat (cname, suffix);
132     }
133   done:
134     /* check for legal name with either group rules or user rules */
135     if ((suffix = strchr(cname, ':'))) {
136         /* check for confusing characters */
137         if (strchr(cname, '\n') ||      /* restrict so recreate can work */
138             strchr(suffix+1, ':'))      /* avoid multiple colons */
139             return PRBADNAM;
140     } else {
141         if (!CorrectUserName (cname)) return PRBADNAM;
142     }
143     return 0;
144 }
145
146 int AccessOK (ut, cid, tentry, mem, any)
147   struct ubik_trans *ut;
148   afs_int32 cid;                                /* caller id */
149   struct prentry *tentry;               /* object being accessed */
150   int mem;                              /* check membership in aid, if group */
151   int any;                              /* if set return true */
152 {   afs_int32 flags;
153     afs_int32 oid;
154     afs_int32 aid;
155
156     if (pr_noAuth) return 1;
157     if (cid == SYSADMINID) return 1;    /* special case fileserver */
158     if (tentry) {
159         flags = tentry->flags;
160         oid = tentry->owner;
161         aid = tentry->id;
162     } else {
163         flags = oid = aid = 0;
164     }
165     if (!(flags & PRACCESS)) {          /* provide default access */
166         if (flags & PRGRP)
167             flags |= PRP_GROUP_DEFAULT;
168         else
169             flags |= PRP_USER_DEFAULT;
170     }
171
172     if (flags & any) return 1;
173     if (oid) {
174         if ((cid == oid) ||
175             IsAMemberOf (ut, cid, oid)) return 1;
176     }
177     if (aid > 0) {                      /* checking on a user */
178         if (aid == cid) return 1;
179     } else if (aid < 0) {               /* checking on group */
180         if ((flags & mem) && IsAMemberOf (ut, cid, aid)) return 1;
181     }
182     /* Allow members of SYSVIEWERID to get membership and status only */
183     if (((mem == PRP_STATUS_MEM)||(mem == PRP_MEMBER_MEM)||
184          (any == PRP_OWNED_ANY))&&(IsAMemberOf (ut, cid, SYSVIEWERID))) 
185         return 1;
186     if (IsAMemberOf (ut, cid, SYSADMINID)) return 1;
187     return 0;                           /* no access */
188 }
189
190 afs_int32 CreateEntry (at, aname, aid, idflag, flag, oid, creator)  
191   struct ubik_trans *at;
192   char aname[PR_MAXNAMELEN];
193   afs_int32 *aid;
194   afs_int32 idflag;
195   afs_int32 flag;
196   afs_int32 oid;
197   afs_int32 creator;
198 {
199     /* get and init a new entry */
200     afs_int32 code;
201     afs_int32 newEntry;
202     struct prentry tentry, tent;
203     char *atsign;
204     
205     memset(&tentry, 0, sizeof(tentry));
206
207     if ((oid == 0) || (oid == ANONYMOUSID)) oid = creator;
208
209     if (flag & PRGRP) {
210         code = CorrectGroupName (at, aname, creator, oid, tentry.name);
211         if (code) return code;
212         if (strcmp (aname, tentry.name) != 0)  return PRBADNAM;
213     } else {                            /* non-group must not have colon */
214         if (!CorrectUserName(aname)) return PRBADNAM;
215         strcpy (tentry.name, aname);
216     }
217
218     if (FindByName(at,aname, &tent)) return PREXIST;
219
220     newEntry = AllocBlock(at);
221     if (!newEntry) return PRDBFAIL;
222 #ifdef PR_REMEMBER_TIMES
223     tentry.createTime = time(0);
224 #endif
225
226     if (flag & PRGRP) {
227         tentry.flags = PRGRP;
228         tentry.owner = oid;
229     } else if (flag == 0) {
230         tentry.flags = 0;
231         tentry.owner = SYSADMINID;
232     } else {
233         return PRBADARG;
234     }
235
236     atsign = strchr(aname, '@');
237     if (!atsign) {
238        /* A normal user or group. Pick an id for it */
239        if (idflag) 
240           tentry.id = *aid;
241        else {
242           code= AllocID(at,flag,&tentry.id);
243           if (code != PRSUCCESS) return code;
244        }
245     } else if (flag & PRGRP) {
246        /* A foreign group. Its format must be AUTHUSER_GROUP@cellname
247         * Then pick an id for the group.
248         */
249        int badFormat;
250             
251        *atsign = '\0';
252        badFormat = strcmp(AUTHUSER_GROUP, aname);
253        *atsign = '@';
254        if (badFormat) return PRBADNAM;
255
256        if (idflag) 
257           tentry.id = *aid;
258        else {
259           code= AllocID(at,flag,&tentry.id);
260           if (code != PRSUCCESS) return code;
261        }
262     } else {
263        /* A foreign user: <name>@<cell>. The foreign user is added to
264         * its representing group. It is 
265         */
266        char *cellGroup;
267        afs_int32 pos, n;
268        struct prentry centry;
269        extern afs_int32 allocNextId();
270
271        /* To create the user <name>@<cell> the group AUTHUSER_GROUP@<cell>
272         * must exist.
273         */
274        cellGroup = (char *)malloc(strlen(AUTHUSER_GROUP) + strlen(atsign) + 1);
275        strcpy(cellGroup, AUTHUSER_GROUP);
276        strcat(cellGroup, atsign);
277        pos = FindByName(at, cellGroup, &centry); 
278        if (!pos) return PRBADNAM;
279        code = pr_Read (at, 0, pos, &centry, sizeof(centry));
280        if (code) return code;
281
282        /* cellid is the id of the group representing the cell */
283        tentry.cellid = ntohl(centry.id); 
284
285        if (idflag) {
286           /* Check if id is good */
287           if (!inRange(&centry,*aid)) return PRBADARG;
288           tentry.id = *aid;
289        } else {
290           /* Allocate an ID special for this foreign user. It is based 
291            * on the representing group's id and nusers count.
292            */
293           tentry.id = allocNextId(&centry);
294        }
295          
296        /* The foreign user will be added to the representing foreign
297         * group. The group can hold up to 30 entries.
298         */
299        if (!(ntohl(centry.flags) & PRQUOTA)) {
300           centry.flags = htonl (ntohl(centry.flags) | PRQUOTA);
301           centry.ngroups = htonl(30);
302        }
303        n = ntohl(centry.ngroups);
304        if ( (n <= 0) && !pr_noAuth ) return PRNOMORE;
305        centry.ngroups = htonl(n - 1);
306
307        /* write updated entry for group */
308        code = pr_Write (at, 0, pos, &centry, sizeof(centry));
309
310        /* Now add the new user entry to the database */
311        tentry.creator = creator;
312        *aid = tentry.id;
313        code = pr_WriteEntry(at, 0, newEntry, &tentry);
314        if (code) return PRDBFAIL;
315        code = AddToIDHash(at, *aid, newEntry);
316        if (code != PRSUCCESS) return code;
317        code = AddToNameHash(at, aname, newEntry);
318        if (code != PRSUCCESS) return code;
319        if (inc_header_word (at, foreigncount, 1)) return PRDBFAIL;
320
321        /* Now add the entry to the authuser group for this cell.
322         * We will reread the entries for the user and the group
323         * instead of modifying them before writing them in the
324         * previous steps. Although not very efficient, much simpler
325         */
326        pos = FindByID(at, tentry.cellid); 
327        if (!pos) return PRBADNAM;
328        code = pr_ReadEntry (at, 0, pos, &centry);
329        if (code) return code;
330        code = AddToEntry(at, &centry, pos, *aid);
331        if (code) return code;
332        /* and now the user entry */
333        pos = FindByID(at,*aid); 
334        if (!pos) return PRBADNAM;
335        code = pr_ReadEntry (at, 0, pos, &tentry);
336        if (code) return code;
337        code = AddToEntry(at, &tentry, pos, tentry.cellid);
338        if (code) return code;
339
340        return PRSUCCESS;
341     }
342
343     /* Remember the largest group id or largest user id */
344     if (flag & PRGRP) {
345         /* group ids are negative */
346         if (tentry.id < (afs_int32)ntohl(cheader.maxGroup)) {
347             code = set_header_word (at, maxGroup, htonl(tentry.id));
348             if (code) return PRDBFAIL;
349         }
350     }
351     else {
352         if (tentry.id > (afs_int32)ntohl(cheader.maxID)) {
353             code = set_header_word (at, maxID, htonl(tentry.id));
354             if (code) return PRDBFAIL;
355         }
356     }
357
358     /* Charge the creator for this group */
359     if (flag & PRGRP) {
360         afs_int32 loc = FindByID (at, creator);
361         struct prentry centry;
362         int admin;
363
364         if (loc) { /* this should only fail during initialization */
365             code = pr_Read (at, 0, loc, &centry, sizeof(centry));
366             if (code) return code;
367
368             /* If quota is uninitialized, do it */
369             if (!(ntohl(centry.flags) & PRQUOTA)) {
370                centry.flags = htonl (ntohl(centry.flags) | PRQUOTA);
371                centry.ngroups = centry.nusers = htonl(20);
372             }
373
374             /* Admins don't get charged for creating a group.
375              * If in noAuth mode, you get changed for it but you 
376              * are still allowed to create as many groups as you want.
377              */
378             admin = ( (creator == SYSADMINID) ||
379                       IsAMemberOf(at,creator,SYSADMINID) );
380             if (!admin) {
381                if (ntohl(centry.ngroups) <= 0) {
382                   if (!pr_noAuth) return PRNOMORE;
383                 } else {
384                    centry.ngroups = htonl(ntohl(centry.ngroups)-1);
385               }
386             }
387
388             code = pr_Write (at, 0, loc, &centry, sizeof(centry));
389             if (code) return code;
390         } /* if (loc) */
391     }
392     else {
393         /* Initialize the quota for the user. Groups don't have their
394          * quota initialized.
395          */
396         tentry.flags |= PRQUOTA;
397         tentry.ngroups = tentry.nusers = 20;
398     }
399
400     tentry.creator = creator;
401     *aid = tentry.id;
402     code = pr_WriteEntry(at, 0, newEntry, &tentry);
403     if (code) return PRDBFAIL;
404     code = AddToIDHash(at,*aid,newEntry);
405     if (code != PRSUCCESS) return code;
406     code = AddToNameHash(at,aname,newEntry);
407     if (code != PRSUCCESS) return code;
408     if (tentry.flags & PRGRP) {
409         code = AddToOwnerChain(at,tentry.id,oid);
410         if (code) return code;
411     }
412     if (tentry.flags & PRGRP) {
413         if (inc_header_word (at, groupcount, 1)) return PRDBFAIL;
414     }
415     else if (tentry.flags & PRINST) {
416         if (inc_header_word (at, instcount, 1)) return PRDBFAIL;
417     }
418     else {
419         if (inc_header_word (at, usercount, 1)) return PRDBFAIL;
420     }
421     return PRSUCCESS;
422 }
423     
424
425 /* RemoveFromEntry - remove aid from bid's entries list, freeing a continuation
426  * entry if appropriate */
427
428 afs_int32 RemoveFromEntry (at, aid, bid)
429   struct ubik_trans *at;
430   afs_int32 aid;
431   afs_int32 bid;
432 {
433     afs_int32 code;
434     struct prentry tentry;
435     struct contentry centry;
436     struct contentry hentry;
437     afs_int32 temp;
438     afs_int32 i,j;
439     afs_int32 nptr;
440     afs_int32 hloc;
441     
442     if (aid == bid) return PRINCONSISTENT;
443     memset(&hentry, 0, sizeof(hentry));
444     temp = FindByID(at,bid);
445     if (temp == 0) return PRNOENT;
446     code = pr_ReadEntry(at, 0, temp, &tentry);
447     if (code != 0) return code;
448 #ifdef PR_REMEMBER_TIMES
449     tentry.removeTime = time(0);
450 #endif
451     for (i=0;i<PRSIZE;i++) {
452         if (tentry.entries[i] == aid) {  
453             tentry.entries[i] = PRBADID;
454             tentry.count--;
455             code = pr_WriteEntry(at,0,temp,&tentry);
456             if (code != 0) return code;
457             return PRSUCCESS;
458         }
459         if (tentry.entries[i] == 0)   /* found end of list */
460             return PRNOENT;
461     }
462     hloc = 0;
463     nptr = tentry.next;
464     while (nptr != 0) {
465         code = pr_ReadCoEntry(at,0,nptr,&centry);
466         if (code != 0) return code;
467         if ((centry.id != bid) || !(centry.flags & PRCONT)) return PRDBBAD;
468         for (i=0;i<COSIZE;i++) {
469             if (centry.entries[i] == aid) {
470                 centry.entries[i] = PRBADID;
471                 for (j=0;j<COSIZE;j++)
472                     if (centry.entries[j] != PRBADID &&
473                         centry.entries[j] != 0) break;
474                 if (j == COSIZE) {   /* can free this block */
475                     if (hloc == 0) {
476                         tentry.next = centry.next;
477                     }
478                     else {
479                         hentry.next = centry.next;
480                         code = pr_WriteCoEntry (at, 0, hloc, &hentry);
481                         if (code != 0) return code;
482                     }
483                     code = FreeBlock (at, nptr);
484                     if (code) return code;
485                 }
486                 else { /* can't free it yet */
487                     code = pr_WriteCoEntry(at,0,nptr,&centry);
488                     if (code != 0) return code;
489                 }
490                 tentry.count--;
491                 code = pr_WriteEntry(at,0,temp,&tentry);
492                 if (code) return PRDBFAIL;
493                 return 0;
494             }
495             if (centry.entries[i] == 0) return PRNOENT;
496         } /* for all coentry slots */
497         hloc = nptr;
498         nptr = centry.next;
499         memcpy(&hentry, &centry, sizeof(centry));
500     } /* while there are coentries */
501     return PRNOENT;
502 }
503
504 /* DeleteEntry - delete the entry in tentry at loc, removing it from all
505  * groups, putting groups owned by it on orphan chain, and freeing the space */
506
507 afs_int32 DeleteEntry (at, tentry, loc)
508   struct ubik_trans *at;
509   struct prentry *tentry;
510   afs_int32 loc;
511 {
512     afs_int32 code;
513     struct contentry centry;
514     afs_int32  i;
515     afs_int32 nptr;
516
517     if (strchr(tentry->name,'@')) {
518        if (tentry->flags & PRGRP) {
519         /* If there are still foreign user accounts from that cell
520            don't delete the group */
521            if (tentry->count) return PRBADARG;
522         } else {
523             /* adjust quota */
524
525           afs_int32 loc = FindByID (at, tentry->cellid);
526           struct prentry centry;
527           if (loc) {
528             code = pr_Read (at, 0, loc, &centry, sizeof(centry));
529             if (code) return code;
530             if (ntohl(centry.flags) & PRQUOTA) {
531                     centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
532             }
533             code = pr_Write (at, 0, loc, &centry, sizeof(centry));
534             if (code) return code;
535           }
536         }
537     }
538     /* First remove the entire membership list */
539     for (i=0;i<PRSIZE;i++) {
540         if (tentry->entries[i] == PRBADID) continue;
541         if (tentry->entries[i] == 0) break;
542         code = RemoveFromEntry (at, tentry->id, tentry->entries[i]);
543         if (code) return code;
544     }
545     nptr = tentry->next;
546     while (nptr != (afs_int32)NULL) {
547         code = pr_ReadCoEntry(at,0,nptr,&centry);
548         if (code != 0) return PRDBFAIL;
549         for (i=0;i<COSIZE;i++) {
550             if (centry.entries[i] == PRBADID) continue;
551             if (centry.entries[i] == 0) break;
552             code = RemoveFromEntry (at, tentry->id, centry.entries[i]);
553             if (code) return code;
554         }
555         code = FreeBlock (at, nptr);    /* free continuation block */
556         if (code) return code;
557         nptr = centry.next;
558     }
559
560     /* Remove us from other's owned chain.  Note that this will zero our owned
561      * field (on disk) so this step must follow the above step in case we are
562      * on our own owned list. */
563     if (tentry->flags & PRGRP) {
564         if (tentry->owner) {
565             code = RemoveFromOwnerChain (at, tentry->id, tentry->owner);
566             if (code) return code;
567         }
568         else {
569             code = RemoveFromOrphan (at, tentry->id);
570             if (code) return code;
571         }
572     }
573
574     code = RemoveFromIDHash(at,tentry->id,&loc);
575     if (code != PRSUCCESS) return code;
576     code = RemoveFromNameHash(at,tentry->name,&loc);
577     if (code != PRSUCCESS) return code;
578
579     if (tentry->flags & PRGRP) {
580         afs_int32 loc = FindByID(at, tentry->creator);
581         struct prentry centry;
582         int admin;
583
584         if (loc) {
585             code = pr_Read (at, 0, loc, &centry, sizeof(centry));
586             if (code) return code;
587             admin = ( (tentry->creator == SYSADMINID) ||
588                       IsAMemberOf(at,tentry->creator,SYSADMINID) );
589             if (ntohl(centry.flags) & PRQUOTA) {
590                 if (!(admin && (ntohl(centry.ngroups) >= 20))) {
591                     centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
592                  }
593             }
594             code = pr_Write (at, 0, loc, &centry, sizeof(centry));
595             if (code) return code;
596         }
597     }
598
599     if (tentry->flags & PRGRP) {
600         if (inc_header_word (at, groupcount, -1)) return PRDBFAIL;
601     }
602     else if (tentry->flags & PRINST) {
603         if (inc_header_word (at, instcount, -1)) return PRDBFAIL;
604     }
605     else {
606         if (strchr(tentry->name,'@')) {
607            if (inc_header_word (at, foreigncount, -1)) return PRDBFAIL;
608         } else {
609           if (inc_header_word (at, usercount, -1)) return PRDBFAIL;
610         }
611     }
612     code = FreeBlock(at, loc);
613     return code;
614 }
615
616 /* AddToEntry - add aid to entry's entries list, alloc'ing a continuation block
617  * if needed.
618  *
619  * Note the entry is written out by this routine. */
620
621 afs_int32 AddToEntry (tt, entry, loc, aid)
622   struct ubik_trans *tt;
623   struct prentry *entry;
624   afs_int32 loc;
625   afs_int32 aid;
626 {
627     afs_int32 code;
628     afs_int32 i;
629     struct contentry nentry;
630     struct contentry aentry;
631     afs_int32 nptr;
632     afs_int32 last;                             /* addr of last cont. block */
633     afs_int32 first = 0;
634     afs_int32 cloc = 0;
635     afs_int32 slot = -1;
636
637     if (entry->id == aid) return PRINCONSISTENT;
638 #ifdef PR_REMEMBER_TIMES
639     entry->addTime = time(0);
640 #endif
641     for (i=0;i<PRSIZE;i++) {
642         if (entry->entries[i] == aid)
643             return PRIDEXIST;
644         if (entry->entries[i] == PRBADID) { /* remember this spot */
645             first = 1;
646             slot = i;
647         }
648         else if (entry->entries[i] == 0) { /* end of the line */
649             if (slot == -1) {
650                 first = 1;
651                 slot = i;
652             }
653             break;
654         }
655     }
656     last = 0;
657     nptr = entry->next;
658     while (nptr != (afs_int32)NULL) {
659         code = pr_ReadCoEntry(tt,0,nptr,&nentry);
660         if (code != 0) return code;
661         last = nptr;
662         if (!(nentry.flags & PRCONT)) return PRDBFAIL;
663         for (i=0;i<COSIZE;i++) {
664             if (nentry.entries[i] == aid)
665                 return PRIDEXIST;
666             if (nentry.entries[i] == PRBADID) {
667                 if (slot == -1) {
668                     slot = i;
669                     cloc = nptr;
670                 }
671             }
672             else if (nentry.entries[i] == 0) {
673                 if (slot == -1) {
674                     slot = i;
675                     cloc = nptr;
676                 }
677                 break;
678             }
679         }
680         nptr = nentry.next;
681     }
682     if (slot != -1) {                   /* we found a place */
683         entry->count++;
684         if (first) {  /* place is in first block */
685             entry->entries[slot] = aid;
686             code = pr_WriteEntry (tt, 0, loc, entry);
687             if (code != 0) return code;
688             return PRSUCCESS;
689         }
690         code = pr_WriteEntry (tt, 0, loc, entry);
691         if (code) return code;
692         code = pr_ReadCoEntry(tt,0,cloc,&aentry);
693         if (code != 0) return code;
694         aentry.entries[slot] = aid;
695         code = pr_WriteCoEntry(tt,0,cloc,&aentry);
696         if (code != 0) return code;
697         return PRSUCCESS;
698     }
699     /* have to allocate a continuation block if we got here */
700     nptr = AllocBlock(tt);
701     if (last) {
702         /* then we should tack new block after last block in cont. chain */
703         nentry.next = nptr;
704         code = pr_WriteCoEntry(tt,0,last,&nentry);
705         if (code != 0) return code;
706     }
707     else {
708         entry->next = nptr;
709     }
710     memset(&aentry, 0, sizeof(aentry));
711     aentry.flags |= PRCONT;
712     aentry.id = entry->id;
713     aentry.next = 0;
714     aentry.entries[0] = aid;
715     code = pr_WriteCoEntry(tt,0,nptr,&aentry);
716     if (code != 0) return code;
717     /* don't forget to update count, here! */
718     entry->count++;
719     code = pr_WriteEntry (tt, 0, loc, entry);
720     return code;
721         
722 }
723
724 afs_int32 AddToPRList (alist, sizeP, id)
725   prlist *alist;
726   int *sizeP;
727   afs_int32 id;
728 {
729     char *tmp;
730     int count;
731
732     if (alist->prlist_len >= *sizeP) {
733         count = alist->prlist_len + 100;
734         if (alist->prlist_val) {
735            tmp = (char *) realloc(alist->prlist_val, count*sizeof(afs_int32));
736         } else {
737            tmp = (char *) malloc(count*sizeof(afs_int32));
738         }
739         if (!tmp) return(PRNOMEM);
740         alist->prlist_val = (afs_int32 *)tmp;
741         *sizeP = count;
742     }
743     alist->prlist_val[alist->prlist_len++] = id;
744     return 0;
745 }
746
747 afs_int32 GetList (at, tentry, alist, add)
748   struct ubik_trans *at;
749   struct prentry *tentry;
750   prlist *alist;
751   afs_int32 add;
752 {
753     afs_int32 code;
754     afs_int32 i;
755     struct contentry centry;
756     afs_int32 nptr;
757     int size;
758     int count = 0;
759
760     size = 0;
761     alist->prlist_val = 0;
762     alist->prlist_len = 0;
763
764     for (i=0;i<PRSIZE;i++) {
765         if (tentry->entries[i] == PRBADID) continue;
766         if (tentry->entries[i] == 0) break;
767         code = AddToPRList (alist, &size, tentry->entries[i]);
768         if (code) return code;
769     }
770
771     for (nptr = tentry->next; nptr != 0; nptr = centry.next) {
772         /* look through cont entries */
773         code = pr_ReadCoEntry(at,0,nptr,&centry);
774         if (code != 0) return code;
775         for (i=0;i<COSIZE;i++) {
776             if (centry.entries[i] == PRBADID) continue;
777             if (centry.entries[i] == 0) break;
778             code = AddToPRList (alist, &size, centry.entries[i]);
779             if (code) return code;
780         }
781         if (count++ > 50) IOMGR_Poll(), count = 0;
782     }
783
784     if (add) { /* this is for a CPS, so tack on appropriate stuff */
785         if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) {
786             if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
787                 (code = AddAuthGroup(tentry, alist, &size)) || 
788                 (code = AddToPRList (alist, &size, tentry->id))) return code;
789         }
790         else {
791             if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
792                 (code = AddToPRList (alist, &size, tentry->id))) return code;
793         }
794     }
795     if (alist->prlist_len > 100) IOMGR_Poll();
796     qsort(alist->prlist_val,alist->prlist_len,sizeof(afs_int32),IDCmp);
797     return PRSUCCESS;
798 }
799
800
801 afs_int32 GetList2 (at, tentry, tentry2 , alist, add)
802   struct ubik_trans *at;
803   struct prentry *tentry;
804   struct prentry *tentry2;
805   prlist *alist;
806   afs_int32 add;
807 {
808     afs_int32 code = 0;
809     afs_int32 i;
810     struct contentry centry;
811     afs_int32 nptr;
812     afs_int32 size;
813     int count = 0;
814
815     size = 0;
816     alist->prlist_val = 0;
817     alist->prlist_len = 0;
818     for (i=0;i<PRSIZE;i++) {
819         if (tentry->entries[i] == PRBADID) continue;
820         if (tentry->entries[i] == 0) break;
821         code = AddToPRList (alist, &size, tentry->entries[i]);
822         if (code) return code;
823     }
824
825     nptr = tentry->next;
826     while (nptr != (afs_uint32)NULL) {
827         /* look through cont entries */
828         code = pr_ReadCoEntry(at,0,nptr,&centry);
829         if (code != 0) return code;
830         for (i=0;i<COSIZE;i++) {
831             if (centry.entries[i] == PRBADID) continue;
832             if (centry.entries[i] == 0) break;
833             code = AddToPRList (alist, &size, centry.entries[i]);
834             if (code) return code;
835         }
836         nptr = centry.next;
837         if (count++ > 50) IOMGR_Poll(), count = 0;
838     }
839
840     for (i=0;i<PRSIZE;i++) {
841         if (tentry2->entries[i] == PRBADID) continue;
842         if (tentry2->entries[i] == 0) break;
843         code = AddToPRList (alist, &size, tentry2->entries[i]);
844         if (code) break;
845     }
846
847     if (!code) {
848             nptr = tentry2->next;
849             while (nptr != (afs_uint32)NULL) {
850                 /* look through cont entries */
851                 code = pr_ReadCoEntry(at,0,nptr,&centry);
852                 if (code != 0) break;
853                 for (i=0;i<COSIZE;i++) {
854                     if (centry.entries[i] == PRBADID) continue;
855                     if (centry.entries[i] == 0) break;
856                     code = AddToPRList (alist, &size, centry.entries[i]);
857                     if (code) break;
858                 }
859                 nptr = centry.next;
860                 if (count++ > 50) IOMGR_Poll(), count = 0;
861             }
862     }
863     if (add) { /* this is for a CPS, so tack on appropriate stuff */
864         if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) {
865             if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
866                 (code = AddToPRList (alist, &size, AUTHUSERID)) ||
867                 (code = AddToPRList (alist, &size, tentry->id))) return code;
868         }
869         else {
870             if ((code = AddToPRList (alist, &size, ANYUSERID)) ||
871                 (code = AddToPRList (alist, &size, tentry->id))) return code;
872         }
873     }
874     if (alist->prlist_len > 100) IOMGR_Poll();
875     qsort(alist->prlist_val,alist->prlist_len,sizeof(afs_int32),IDCmp);
876     return PRSUCCESS;
877 }
878
879 afs_int32 GetOwnedChain (ut, next, alist)
880   struct ubik_trans *ut;
881   afs_int32 *next;
882   prlist *alist;
883 {   afs_int32 code;
884     struct prentry tentry;
885     int size;
886     int count = 0;
887
888     size = 0;
889     alist->prlist_val = 0;
890     alist->prlist_len = 0;
891
892     for (; *next; *next = ntohl(tentry.nextOwned)) {
893         code = pr_Read (ut, 0, *next, &tentry, sizeof(tentry));
894         if (code) return code;
895         code = AddToPRList (alist, &size, ntohl(tentry.id));
896         if (alist->prlist_len >= PR_MAXGROUPS) {
897            return PRTOOMANY;
898         }
899         if (code) return code;
900         if (count++ > 50) IOMGR_Poll(), count = 0;
901     }
902     if (alist->prlist_len > 100) IOMGR_Poll();
903     qsort(alist->prlist_val,alist->prlist_len,sizeof(afs_int32),IDCmp);
904     return PRSUCCESS;
905 }
906
907 afs_int32 GetMax(at,uid,gid)
908 struct ubik_trans *at;
909 afs_int32 *uid;
910 afs_int32 *gid;
911 {
912     *uid = ntohl(cheader.maxID);
913     *gid = ntohl(cheader.maxGroup);
914     return PRSUCCESS;
915 }
916
917 afs_int32 SetMax(at,id,flag)
918 struct ubik_trans *at;
919 afs_int32 id;
920 afs_int32 flag;
921 {
922     afs_int32 code;
923     if (flag & PRGRP) {
924         cheader.maxGroup = htonl(id);
925         code = pr_Write(at,0,16,(char *)&cheader.maxGroup,sizeof(cheader.maxGroup));
926         if (code != 0) return code;
927     }
928     else {
929         cheader.maxID = htonl(id);
930         code = pr_Write(at,0,20,(char *)&cheader.maxID,sizeof(cheader.maxID));
931         if (code != 0) return code;
932     }
933     return PRSUCCESS;
934 }
935
936 afs_int32 read_DbHeader(tt)
937      struct ubik_trans *tt;
938 {
939     afs_int32 code;
940
941     if (!ubik_CacheUpdate(tt)) return 0;
942
943     code = pr_Read(tt, 0, 0, (char *)&cheader, sizeof(cheader));
944     if (code != 0) {
945         com_err (whoami, code, "Couldn't read header");
946     }
947     return code;
948 }
949
950 int pr_noAuth;
951 afs_int32 initd=0;
952
953 afs_int32 Initdb()
954 {
955     afs_int32 code;
956     struct ubik_trans *tt;
957     afs_int32 len;
958
959     /* init the database.  We'll try reading it, but if we're starting
960      * from scratch, we'll have to do a write transaction. */
961
962     pr_noAuth = afsconf_GetNoAuthFlag(prdir);
963
964     code = ubik_BeginTransReadAny(dbase,UBIK_READTRANS, &tt);
965     if (code) return code;
966     code = ubik_SetLock(tt,1,1,LOCKREAD);
967     if (code) {
968         ubik_AbortTrans(tt);
969         return code;
970     }
971     if (!initd) {
972         initd = 1;
973     } else if (!ubik_CacheUpdate (tt)) {
974         code = ubik_EndTrans(tt);
975         return code;
976     }
977
978     len = sizeof(cheader);
979     code = pr_Read(tt, 0, 0, (char *) &cheader, len);
980     if (code != 0) {
981         com_err (whoami, code, "couldn't read header");
982         ubik_AbortTrans(tt);
983         return code;
984     }
985     if ((ntohl(cheader.version) == PRDBVERSION) &&
986         ntohl(cheader.headerSize) == sizeof(cheader) &&
987         ntohl(cheader.eofPtr) != (afs_uint32)NULL &&
988         FindByID(tt,ANONYMOUSID) != 0){
989         /* database exists, so we don't have to build it */
990         code = ubik_EndTrans(tt);
991         if (code) return code;
992         return PRSUCCESS;
993     }
994     /* else we need to build a database */
995     code = ubik_EndTrans(tt);
996     if (code) return code;
997
998     /* Only rebuild database if the db was deleted (the header is zero) and we
999        are running noAuth. */
1000     {   char *bp = (char *)&cheader;
1001         int i;
1002         for (i=0; i<sizeof(cheader); i++)
1003             if (bp[i])  {
1004                 code = PRDBBAD;
1005                 com_err (whoami, code,
1006                          "Can't rebuild database because it is not empty");
1007                 return code;
1008             }
1009     }
1010     if (!pr_noAuth) {
1011         code = PRDBBAD;
1012         com_err (whoami, code,
1013                  "Can't rebuild database because not running NoAuth");
1014         return code;
1015     }
1016
1017     code = ubik_BeginTrans(dbase,UBIK_WRITETRANS, &tt);
1018     if (code) return code;
1019
1020     code = ubik_SetLock(tt,1,1,LOCKWRITE);
1021     if (code) {
1022         ubik_AbortTrans(tt);
1023         return code;
1024     }
1025
1026     /* before doing a rebuild, check again that the dbase looks bad, because
1027      * the previous check was only under a ReadAny transaction, and there could
1028      * actually have been a good database out there.  Now that we have a
1029      * real write transaction, make sure things are still bad.
1030      */
1031     if ((ntohl(cheader.version) == PRDBVERSION) &&
1032         ntohl(cheader.headerSize) == sizeof(cheader) &&
1033         ntohl(cheader.eofPtr) != (afs_uint32)NULL &&
1034         FindByID(tt,ANONYMOUSID) != 0){
1035         /* database exists, so we don't have to build it */
1036         code = ubik_EndTrans(tt);
1037         if (code) return code;
1038         return PRSUCCESS;
1039     }
1040
1041     /* Initialize the database header */
1042     if ((code = set_header_word (tt, version, htonl(PRDBVERSION))) ||
1043         (code = set_header_word (tt, headerSize, htonl(sizeof(cheader)))) ||
1044         (code = set_header_word (tt, eofPtr, cheader.headerSize))) {
1045         com_err (whoami, code, "couldn't write header words");
1046         ubik_AbortTrans(tt);
1047         return code;
1048     }
1049
1050 #define InitialGroup(id,name) do {    \
1051     afs_int32 temp = (id);                    \
1052     afs_int32 flag = (id) < 0 ? PRGRP : 0; \
1053     code = CreateEntry                \
1054         (tt, (name), &temp, /*idflag*/1, flag, SYSADMINID, SYSADMINID); \
1055     if (code) {                       \
1056         com_err (whoami, code, "couldn't create %s with id %di.",       \
1057                  (name), (id));       \
1058         ubik_AbortTrans(tt);          \
1059         return code;                  \
1060     }                                 \
1061 } while (0)
1062
1063     InitialGroup (SYSADMINID, "system:administrators");
1064     InitialGroup (SYSBACKUPID, "system:backup");
1065     InitialGroup (ANYUSERID, "system:anyuser");
1066     InitialGroup (AUTHUSERID, "system:authuser");
1067     InitialGroup (SYSVIEWERID, "system:ptsviewers");
1068     InitialGroup (ANONYMOUSID, "anonymous");
1069
1070     /* Well, we don't really want the max id set to anonymousid, so we'll set
1071      * it back to 0 */
1072     code = set_header_word (tt, maxID, 0); /* correct in any byte order */
1073     if (code) {
1074         com_err (whoami, code, "couldn't reset max id");
1075         ubik_AbortTrans(tt);
1076         return code;
1077     }
1078
1079     code = ubik_EndTrans(tt);
1080     if (code) return code;
1081     return PRSUCCESS;
1082 }
1083
1084 afs_int32 ChangeEntry (at, aid, cid, name, oid, newid)
1085   struct ubik_trans *at;
1086   afs_int32 aid;
1087   afs_int32 cid;
1088   char *name;
1089   afs_int32 oid;
1090   afs_int32 newid;
1091 {
1092     afs_int32 code;
1093     afs_int32 i, nptr, pos;
1094     struct contentry centry;
1095     struct prentry tentry, tent;
1096     afs_int32 loc;
1097     afs_int32 oldowner;
1098     char holder[PR_MAXNAMELEN];
1099     char temp[PR_MAXNAMELEN];
1100     char oldname[PR_MAXNAMELEN];
1101     char *atsign;
1102
1103     memset(holder, 0, PR_MAXNAMELEN);
1104     memset(temp, 0, PR_MAXNAMELEN);
1105     loc = FindByID(at,aid);
1106     if (!loc) return PRNOENT;
1107     code = pr_ReadEntry(at,0,loc,&tentry);
1108     if (code) return PRDBFAIL;
1109     if (tentry.owner != cid &&
1110         !IsAMemberOf(at,cid,SYSADMINID) &&
1111         !IsAMemberOf(at,cid,tentry.owner) &&
1112         !pr_noAuth) return PRPERM;
1113 #ifdef PR_REMEMBER_TIMES
1114     tentry.changeTime = time(0);
1115 #endif
1116
1117     /* we're actually trying to change the id */
1118     if (newid && (newid != aid)) {
1119         if (!IsAMemberOf(at,cid,SYSADMINID) && !pr_noAuth) return PRPERM;
1120
1121         pos = FindByID(at,newid);
1122         if (pos) return PRIDEXIST;  /* new id already in use! */
1123         if ((aid < 0 && newid > 0) || (aid > 0 && newid < 0)) return PRPERM;
1124
1125         /* Should check that foreign users id to change to is good: inRange() */
1126
1127         /* if new id is not in use, rehash things */
1128         code = RemoveFromIDHash(at,aid,&loc);
1129         if (code != PRSUCCESS) return code;
1130         tentry.id = newid;
1131         code = pr_WriteEntry(at,0,loc,&tentry);
1132         if (code) return code;
1133         code = AddToIDHash(at,tentry.id,loc);
1134         if (code) return code;
1135
1136         /* get current data */
1137         code = pr_ReadEntry(at, 0, loc, &tentry);
1138         if (code) return PRDBFAIL;
1139
1140         /* Also change the references from the membership list */
1141         for (i=0; i<PRSIZE; i++) {
1142            if (tentry.entries[i] == PRBADID) continue;
1143            if (tentry.entries[i] == 0) break;
1144            pos = FindByID(at, tentry.entries[i]);
1145            if (!pos) return(PRDBFAIL);
1146            code = RemoveFromEntry(at, aid, tentry.entries[i]);
1147            if (code) return code;
1148            code = pr_ReadEntry(at, 0, pos, &tent);
1149            if (code) return code;
1150            code = AddToEntry(at, &tent, pos, newid);
1151            if (code) return code;
1152         }
1153         /* Look through cont entries too. This needs to be broken into
1154          * seperate transaction so that no one transaction becomes too 
1155          * large to complete.
1156          */
1157         for (nptr=tentry.next; nptr; nptr=centry.next) {
1158            code = pr_ReadCoEntry(at, 0, nptr, &centry);
1159            if (code) return code;
1160            for (i=0; i<COSIZE; i++) {
1161               if (centry.entries[i] == PRBADID) continue;
1162               if (centry.entries[i] == 0) break;
1163               pos = FindByID(at, centry.entries[i]);
1164               if (!pos) return(PRDBFAIL);
1165               code = RemoveFromEntry(at, aid, centry.entries[i]);
1166               if (code) return code;
1167               code = pr_ReadEntry(at, 0, pos, &tent);
1168               if (code) return code;
1169               code = AddToEntry(at, &tent, pos, newid);
1170               if (code) return code;
1171            }
1172        }
1173     }
1174
1175     atsign = strchr(tentry.name, '@'); /* check for foreign entry */
1176
1177     /* Change the owner */
1178     if (oid && (oid != tentry.owner)) {
1179         /* only groups can have their owner's changed */
1180         if (!(tentry.flags & PRGRP)) return PRPERM;
1181         if (atsign != NULL) return PRPERM;
1182         oldowner = tentry.owner;
1183         tentry.owner = oid;
1184         /* The entry must be written through first so Remove and Add routines
1185          * can operate on disk data */
1186         code = pr_WriteEntry(at,0,loc,(char *)&tentry);
1187         if (code) return PRDBFAIL;
1188
1189         /* switch owner chains */
1190         if (oldowner)           /* if it has an owner */
1191            code = RemoveFromOwnerChain(at,tentry.id,oldowner);
1192         else                    /* must be an orphan */
1193            code = RemoveFromOrphan(at,tentry.id);
1194         if (code) return code;
1195         code = AddToOwnerChain(at,tentry.id,tentry.owner);
1196         if (code) return code;
1197
1198         /* fix up the name */
1199         if (strlen(name) == 0) name = tentry.name;
1200         /* get current data */
1201         code = pr_ReadEntry(at,0,loc,&tentry);
1202         if (code) return PRDBFAIL;
1203     }
1204
1205     /* Change the name, if name is a ptr to tentry.name then this name change
1206      * is due to a chown, otherwise caller has specified a new name */
1207     if ((name == tentry.name) ||
1208         (*name && (strcmp (tentry.name, name) != 0))) {
1209         strncpy (oldname, tentry.name, PR_MAXNAMELEN);
1210         if (tentry.flags & PRGRP) {
1211             /* don't let foreign cell groups change name */
1212             if (atsign != NULL) return PRPERM;
1213             code = CorrectGroupName (at, name, cid, tentry.owner, tentry.name);
1214             if (code) return code;
1215
1216             if (name == tentry.name) {  /* owner fixup */
1217                 if (strcmp (oldname, tentry.name) == 0) goto nameOK;
1218             } else {                    /* new name, caller must be correct */
1219                 if (strcmp (name, tentry.name) != 0) return PRBADNAM;
1220             }
1221         }
1222         else
1223         /* Allow a foreign name change only if the cellname part is
1224            the same */
1225         {
1226             char *newatsign;
1227
1228             newatsign = strchr(name, '@');
1229             if (newatsign != atsign){ /* if they are the same no problem*/
1230                /*if the pointers are not equal the strings better be */
1231                if ((atsign == NULL) || (newatsign == NULL) ||
1232                     strcmp (atsign,newatsign)) return PRPERM;
1233             } 
1234             if (!CorrectUserName(name)) return PRBADNAM;
1235         }
1236
1237         pos = FindByName(at,name, &tent);
1238         if (pos) return PREXIST;
1239         code = RemoveFromNameHash (at, oldname, &loc);
1240         if (code != PRSUCCESS) return code;
1241         strncpy (tentry.name, name, PR_MAXNAMELEN);
1242         code = pr_WriteEntry(at,0,loc,(char *)&tentry);
1243         if (code) return PRDBFAIL;
1244         code = AddToNameHash(at,tentry.name,loc);
1245         if (code != PRSUCCESS) return code;
1246 nameOK:;
1247     }
1248     return PRSUCCESS;
1249 }
1250
1251
1252 afs_int32 allocNextId(cellEntry)
1253      struct prentry *cellEntry;
1254 {
1255         /* Id's for foreign cell entries are constructed as follows:
1256            The 16 low order bits are the group id of the cell and the
1257            top 16 bits identify the particular users in that cell */
1258
1259         afs_int32 id;
1260
1261
1262         id = (ntohl(cellEntry -> nusers) +1); 
1263         cellEntry->nusers = htonl(id);
1264                                       /* use the field nusers to keep 
1265                                          the next available id in that
1266                                          foreign cell's group. Note :
1267                                          It would seem more appropriate
1268                                          to use ngroup for that and nusers
1269                                          to enforce the quota, however pts
1270                                          does not have an option to change 
1271                                          foreign users quota yet  */
1272
1273         id = (id << 16) | ((ntohl(cellEntry-> id)) & 0x0000ffff);
1274         return id;
1275 }
1276
1277 int inRange(cellEntry,aid)
1278 struct prentry *cellEntry;
1279 afs_int32 aid;
1280 {
1281         afs_uint32 id,cellid,groupid;
1282
1283
1284         /*
1285           The only thing that we want to make sure here is that
1286           the id is in the legal range of this group. If it is
1287           a duplicate we don't care since it will get caught 
1288           in a different check.
1289         */
1290
1291         cellid = aid & 0x0000ffff;
1292         groupid =  (ntohl(cellEntry-> id)) & 0x0000ffff;
1293         if (cellid != groupid) return 0; /* not in range */
1294
1295         /* 
1296            if we got here we're ok but we need to update the nusers
1297            field in order to get the id correct the next time that 
1298            we try to allocate it automatically
1299         */
1300
1301         id = aid >> 16;
1302         if (id > ntohl(cellEntry -> nusers))
1303                 cellEntry -> nusers = htonl(id);
1304         return 1;
1305
1306 }
1307
1308 AddAuthGroup(tentry, alist, size)
1309   struct prentry *tentry;
1310   prlist *alist;
1311   afs_int32 *size;
1312 {
1313         if (!(strchr(tentry->name, '@'))) 
1314              return (AddToPRList (alist, size, AUTHUSERID));
1315         else 
1316              return PRSUCCESS;
1317 }