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