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