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