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