pts mem -expandgroups option
[openafs.git] / src / ptserver / ptuser.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 #if defined(UKERNEL)
12 #include "afs/param.h"
13 #else
14 #include <afs/param.h>
15 #endif
16
17
18 #if defined(UKERNEL)
19 #include "afs/sysincludes.h"
20 #include "afs_usrops.h"
21 #include "afsincludes.h"
22 #include "afs/stds.h"
23 #include "rx/rx.h"
24 #include "rx/xdr.h"
25 #include "afs/auth.h"
26 #include "afs/cellconfig.h"
27 #include "afs/afsutil.h"
28 #include "afs/ptclient.h"
29 #include "afs/ptuser.h"
30 #include "afs/pterror.h"
31 #include "afs/com_err.h"
32 #else /* defined(UKERNEL) */
33 #include <afs/stds.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #ifdef AFS_NT40_ENV
37 #include <winsock2.h>
38 #else
39 #include <netinet/in.h>
40 #endif
41 #include <stdio.h>
42 #include <string.h>
43 #include <rx/rx.h>
44 #include <rx/xdr.h>
45 #include <afs/auth.h>
46 #include <afs/cellconfig.h>
47 #include <afs/afsutil.h>
48 #include <afs/com_err.h>
49 #include <errno.h>
50 #include "ptclient.h"
51 #include "ptuser.h"
52 #include "pterror.h"
53 #endif /* defined(UKERNEL) */
54
55
56 struct ubik_client *pruclient = 0;
57 static afs_int32 lastLevel;     /* security level pruclient, if any */
58
59 static char *whoami = "libprot";
60
61
62 #define ID_HASH_SIZE 1024
63 #define ID_STACK_SIZE 1024
64
65 /**
66  * Hash table chain of user and group ids.
67  */
68 struct idchain {
69     struct idchain *next;
70     afs_int32 id;
71 };
72
73 /**
74  * Hash table of user and group ids.
75  */
76 struct idhash {
77     afs_uint32 userEntries;         /**< number of user id entries hashed */
78     afs_uint32 groupEntries;        /**< number of group id entries hashed */
79     struct idchain *hash[ID_HASH_SIZE];
80 };
81
82 /**
83  * Allocate a new id hash table.
84  */
85 static afs_int32
86 AllocateIdHash(struct idhash **aidhash)
87 {
88     struct idhash *idhash;
89
90     idhash = (struct idhash *)malloc(sizeof(struct idhash));
91     if (!idhash) {
92        return ENOMEM;
93     }
94     memset((void *)idhash, 0, sizeof(struct idhash));
95     *aidhash = idhash;
96     return 0;
97 }
98
99 /**
100  * Free the id hash.
101  */
102 static void
103 FreeIdHash(struct idhash *idhash)
104 {
105     int index;
106     struct idchain *chain;
107     struct idchain *next;
108
109     for (index = 0; index < ID_HASH_SIZE; index++) {
110        for (chain = idhash->hash[index]; chain; chain = next) {
111            next = chain->next;
112            free(chain);
113        }
114     }
115     free(idhash);
116 }
117
118 /**
119  * Indicate if group/user id is already hashed, and
120  * if not insert it.
121  *
122  * @returns whether id is present
123  *   @retval >0 id is already present in the hash
124  *   @retval 0  id was not found and was inserted into the hash
125  *   @retval <0 error encountered
126  */
127 static afs_int32
128 FindId(struct idhash *idhash, afs_int32 id)
129 {
130     afs_int32 index;
131     struct idchain *chain;
132     struct idchain *newChain;
133
134     index = abs(id) % ID_HASH_SIZE;
135     for (chain = idhash->hash[index]; chain; chain = chain->next) {
136         if (chain->id == id) {
137             return 1;
138         }
139     }
140
141     /* Insert this id but return not found. */
142     newChain = (struct idchain *)malloc(sizeof(struct idchain));
143     if (!newChain) {
144         return ENOMEM;
145     } else {
146         newChain->id = id;
147         newChain->next = idhash->hash[index];
148         idhash->hash[index] = newChain;
149         if (id < 0) {
150             idhash->groupEntries++;
151         } else {
152             idhash->userEntries++;
153         }
154     }
155     return 0;
156 }
157
158 /**
159  * Create an idlist from the ids in the hash.
160  */
161 static afs_int32
162 CreateIdList(struct idhash *idhash, idlist * alist, afs_int32 select)
163 {
164     struct idchain *chain;
165     afs_int32 entries = 0;
166     int index;
167     int i;
168
169     if (select & PRGROUPS) {
170         entries += idhash->groupEntries;
171     }
172     if (select & PRUSERS) {
173         entries += idhash->userEntries;
174     }
175
176     alist->idlist_len = entries;
177     alist->idlist_val = (afs_int32 *) malloc(sizeof(afs_int32) * entries);
178     if (!alist->idlist_val) {
179         return ENOMEM;
180     }
181
182     for (i = 0, index = 0; index < ID_HASH_SIZE; index++) {
183         for (chain = idhash->hash[index]; chain; chain = chain->next) {
184             if (chain->id < 0) {
185                 if (select & PRGROUPS) {
186                     alist->idlist_val[i++] = chain->id;
187                 }
188             } else {
189                 if (select & PRUSERS) {
190                     alist->idlist_val[i++] = chain->id;
191                 }
192             }
193         }
194     }
195     return 0;
196 }
197
198 afs_int32
199 pr_Initialize(IN afs_int32 secLevel, IN const char *confDir, IN char *cell)
200 {
201     afs_int32 code;
202     struct rx_connection *serverconns[MAXSERVERS];
203     struct rx_securityClass *sc = NULL;
204     static struct afsconf_dir *tdir = (struct afsconf_dir *)NULL;       /* only do this once */
205     static char tconfDir[100] = "";
206     static char tcell[64] = "";
207     afs_int32 scIndex;
208     afs_int32 secFlags;
209     static struct afsconf_cell info;
210     afs_int32 i;
211 #if !defined(UKERNEL)
212     char cellstr[64];
213 #endif
214     afs_int32 gottdir = 0;
215     afs_int32 refresh = 0;
216
217     initialize_PT_error_table();
218     initialize_RXK_error_table();
219     initialize_ACFG_error_table();
220     initialize_KTC_error_table();
221
222 #if defined(UKERNEL)
223     if (!cell) {
224         cell = afs_LclCellName;
225     }
226 #else /* defined(UKERNEL) */
227     if (!cell) {
228         if (!tdir) 
229             tdir = afsconf_Open(confDir);
230         if (!tdir) {
231             if (confDir && strcmp(confDir, ""))
232                 fprintf(stderr,
233                         "%s: Could not open configuration directory: %s.\n",
234                         whoami, confDir);
235             else
236                 fprintf(stderr,
237                         "%s: No configuration directory specified.\n",
238                         whoami);
239             return -1;
240         }
241         gottdir = 1;
242
243         code = afsconf_GetLocalCell(tdir, cellstr, sizeof(cellstr));
244         if (code) {
245             fprintf(stderr,
246                      "libprot: Could not get local cell. [%d]\n", code);
247             return code;
248         }
249         cell = cellstr;
250     }
251 #endif /* defined(UKERNEL) */
252
253     if (tdir == NULL || strcmp(confDir, tconfDir) || strcmp(cell, tcell)) {
254         /*
255          * force re-evaluation.  we either don't have an afsconf_dir,
256          * the directory has changed or the cell has changed.
257          */
258         if (tdir && !gottdir) {
259             afsconf_Close(tdir);
260             tdir = (struct afsconf_dir *)NULL;
261         }
262         pruclient = (struct ubik_client *)NULL;
263         refresh = 1;
264     }
265
266     if (refresh) {
267         strncpy(tconfDir, confDir, sizeof(tconfDir));
268         strncpy(tcell, cell, sizeof(tcell));
269
270 #if defined(UKERNEL)
271         tdir = afs_cdir;
272 #else /* defined(UKERNEL) */
273         if (!gottdir)
274             tdir = afsconf_Open(confDir);
275         if (!tdir) {
276             if (confDir && strcmp(confDir, ""))
277                 fprintf(stderr,
278                         "libprot: Could not open configuration directory: %s.\n",
279                         confDir);
280             else
281                 fprintf(stderr,
282                         "libprot: No configuration directory specified.\n");
283             return -1;
284         }
285 #endif /* defined(UKERNEL) */
286
287         code = afsconf_GetCellInfo(tdir, cell, "afsprot", &info);
288         if (code) {
289             fprintf(stderr, "libprot: Could not locate cell %s in %s/%s\n",
290                     cell, confDir, AFSDIR_CELLSERVDB_FILE);
291             return code;
292         }
293     }
294
295     /* If we already have a client and it is at the security level we
296      * want, don't get a new one. Unless the security level is 2 in
297      * which case we will get one (and re-read the key file).
298      */
299     if (pruclient && (lastLevel == secLevel) && (secLevel != 2)) {
300         return 0;
301     }
302
303     code = rx_Init(0);
304     if (code) {
305         fprintf(stderr, "libprot:  Could not initialize rx.\n");
306         return code;
307     }
308
309     /* Most callers use secLevel==1, however, the fileserver uses secLevel==2
310      * to force use of the KeyFile.  secLevel == 0 implies -noauth was
311      * specified. */
312     if (secLevel == 2) {
313         code = afsconf_GetLatestKey(tdir, 0, 0);
314         if (code) {
315             afs_com_err(whoami, code, "(getting key from local KeyFile)\n");
316         } else {
317             /* If secLevel is two assume we're on a file server and use
318              * ClientAuthSecure if possible. */
319             code = afsconf_ClientAuthSecure(tdir, &sc, &scIndex);
320             if (code)
321                 afs_com_err(whoami, code, "(calling client secure)\n");
322         }
323     } else if (secLevel > 0) {
324         secFlags = 0;
325         if (secLevel > 1)
326             secFlags |= AFSCONF_SECOPTS_ALWAYSENCRYPT;
327
328         code = afsconf_ClientAuthToken(&info, secFlags, &sc, &scIndex, NULL);
329         if (code) {
330             afs_com_err(whoami, code, "(getting token)");
331             if (secLevel > 1)
332                 return code;
333         }
334     }
335
336     if (sc == NULL) {
337         sc = rxnull_NewClientSecurityObject();
338         scIndex = RX_SECIDX_NULL;
339     }
340
341     if ((scIndex == RX_SECIDX_NULL) && (secLevel != 0))
342         fprintf(stderr,
343                 "%s: Could not get afs tokens, running unauthenticated\n",
344                 whoami);
345
346     memset(serverconns, 0, sizeof(serverconns));        /* terminate list!!! */
347     for (i = 0; i < info.numServers; i++)
348         serverconns[i] =
349             rx_NewConnection(info.hostAddr[i].sin_addr.s_addr,
350                              info.hostAddr[i].sin_port, PRSRV, sc,
351                              scIndex);
352
353     code = ubik_ClientInit(serverconns, &pruclient);
354     if (code) {
355         afs_com_err(whoami, code, "ubik client init failed.");
356         return code;
357     }
358     lastLevel = scIndex;
359
360     code = rxs_Release(sc);
361     return code;
362 }
363
364 int
365 pr_End(void)
366 {
367     int code = 0;
368
369     if (pruclient) {
370         code = ubik_ClientDestroy(pruclient);
371         pruclient = 0;
372     }
373     return code;
374 }
375
376
377
378 int
379 pr_CreateUser(char name[PR_MAXNAMELEN], afs_int32 *id)
380 {
381     register afs_int32 code;
382
383     stolower(name);
384     if (*id) {
385         code = ubik_PR_INewEntry(pruclient, 0, name, *id, 0);
386         return code;
387     } else {
388         code = ubik_PR_NewEntry(pruclient, 0, name, 0, 0, id);
389         return code;
390     }
391
392 }
393
394 int 
395 pr_CreateGroup(char name[PR_MAXNAMELEN], char owner[PR_MAXNAMELEN], afs_int32 *id)
396 {
397     register afs_int32 code;
398     afs_int32 oid = 0;
399     afs_int32 flags = 0;
400
401     stolower(name);
402     if (owner) {
403         code = pr_SNameToId(owner, &oid);
404         if (code)
405             return code;
406         if (oid == ANONYMOUSID)
407             return PRNOENT;
408     }
409     flags |= PRGRP;
410     if (*id) {
411         code = ubik_PR_INewEntry(pruclient, 0, name, *id, oid);
412         return code;
413     } else {
414         code = ubik_PR_NewEntry(pruclient, 0, name, flags, oid, id);
415         return code;
416     }
417 }
418
419 int
420 pr_Delete(char *name)
421 {
422     register afs_int32 code;
423     afs_int32 id;
424
425     stolower(name);
426     code = pr_SNameToId(name, &id);
427     if (code)
428         return code;
429     if (id == ANONYMOUSID)
430         return PRNOENT;
431     code = ubik_PR_Delete(pruclient, 0, id);
432     return code;
433 }
434
435 int
436 pr_DeleteByID(afs_int32 id)
437 {
438     register afs_int32 code;
439
440     code = ubik_PR_Delete(pruclient, 0, id);
441     return code;
442 }
443
444 int
445 pr_AddToGroup(char *user, char *group)
446 {
447     register afs_int32 code;
448     namelist lnames;
449     idlist lids;
450
451     lnames.namelist_len = 2;
452     lnames.namelist_val = malloc(2 * PR_MAXNAMELEN);
453     strncpy(lnames.namelist_val[0], user, PR_MAXNAMELEN);
454     strncpy(lnames.namelist_val[1], group, PR_MAXNAMELEN);
455     lids.idlist_val = 0;
456     lids.idlist_len = 0;
457     code = pr_NameToId(&lnames, &lids);
458     if (code)
459         goto done;
460     /* if here, still could be missing an entry */
461     if (lids.idlist_val[0] == ANONYMOUSID
462         || lids.idlist_val[1] == ANONYMOUSID) {
463         code = PRNOENT;
464         goto done;
465     }
466     code =
467         ubik_PR_AddToGroup(pruclient, 0, lids.idlist_val[0],
468                   lids.idlist_val[1]);
469   done:
470     if (lnames.namelist_val)
471         free(lnames.namelist_val);
472
473     xdr_free((xdrproc_t) xdr_idlist, &lids);
474     return code;
475 }
476
477 int
478 pr_RemoveUserFromGroup(char *user, char *group)
479 {
480     register afs_int32 code;
481     namelist lnames;
482     idlist lids;
483
484     lnames.namelist_len = 2;
485     lnames.namelist_val = malloc(2 * PR_MAXNAMELEN);
486     strncpy(lnames.namelist_val[0], user, PR_MAXNAMELEN);
487     strncpy(lnames.namelist_val[1], group, PR_MAXNAMELEN);
488     lids.idlist_val = 0;
489     lids.idlist_len = 0;
490     code = pr_NameToId(&lnames, &lids);
491     if (code)
492         goto done;
493
494     if (lids.idlist_val[0] == ANONYMOUSID
495         || lids.idlist_val[1] == ANONYMOUSID) {
496         code = PRNOENT;
497         goto done;
498     }
499     code =
500         ubik_PR_RemoveFromGroup(pruclient, 0, lids.idlist_val[0],
501                   lids.idlist_val[1]);
502   done:
503     if (lnames.namelist_val)
504         free(lnames.namelist_val);
505
506     xdr_free((xdrproc_t) xdr_idlist, &lids);
507
508     return code;
509 }
510
511 int
512 pr_NameToId(namelist *names, idlist *ids)
513 {
514     register afs_int32 code;
515     register afs_int32 i;
516
517     for (i = 0; i < names->namelist_len; i++)
518         stolower(names->namelist_val[i]);
519     code = ubik_PR_NameToID(pruclient, 0, names, ids);
520     return code;
521 }
522
523 int
524 pr_SNameToId(char name[PR_MAXNAMELEN], afs_int32 *id)
525 {
526     namelist lnames;
527     idlist lids;
528     register afs_int32 code;
529
530     lids.idlist_len = 0;
531     lids.idlist_val = 0;
532     lnames.namelist_len = 1;
533     lnames.namelist_val = malloc(PR_MAXNAMELEN);
534     stolower(name);
535     strncpy(lnames.namelist_val[0], name, PR_MAXNAMELEN);
536     code = ubik_PR_NameToID(pruclient, 0, &lnames, &lids);
537     if (lids.idlist_val) {
538         *id = *lids.idlist_val;
539         xdr_free((xdrproc_t) xdr_idlist, &lids);
540     }
541     if (lnames.namelist_val)
542         free(lnames.namelist_val);
543     return code;
544 }
545
546 int
547 pr_IdToName(idlist *ids, namelist *names)
548 {
549     register afs_int32 code;
550
551     code = ubik_PR_IDToName(pruclient, 0, ids, names);
552     return code;
553 }
554
555 int
556 pr_SIdToName(afs_int32 id, char name[PR_MAXNAMELEN])
557 {
558     namelist lnames;
559     idlist lids;
560     register afs_int32 code;
561
562     lids.idlist_len = 1;
563     lids.idlist_val = malloc(sizeof(afs_int32));
564     *lids.idlist_val = id;
565     lnames.namelist_len = 0;
566     lnames.namelist_val = 0;
567     code = ubik_PR_IDToName(pruclient, 0, &lids, &lnames);
568
569     if (lnames.namelist_val)
570         strncpy(name, lnames.namelist_val[0], PR_MAXNAMELEN);
571
572     if (lids.idlist_val)
573         free(lids.idlist_val);
574
575     xdr_free((xdrproc_t) xdr_namelist, &lnames);
576
577     return code;
578 }
579
580 int
581 pr_GetCPS(afs_int32 id, prlist *CPS)
582 {
583     register afs_int32 code;
584     afs_int32 over;
585
586     over = 0;
587     code = ubik_PR_GetCPS(pruclient, 0, id, CPS, &over);
588     if (code != PRSUCCESS)
589         return code;
590     if (over) {
591         /* do something about this, probably make a new call */
592         /* don't forget there's a hard limit in the interface */
593         fprintf(stderr, "membership list for id %d exceeds display limit\n",
594                 id);
595     }
596     return 0;
597 }
598
599 int
600 pr_GetCPS2(afs_int32 id, afs_int32 host, prlist *CPS)
601 {
602     register afs_int32 code;
603     afs_int32 over;
604
605     over = 0;
606     code = ubik_PR_GetCPS2(pruclient, 0, id, host, CPS, &over);
607     if (code != PRSUCCESS)
608         return code;
609     if (over) {
610         /* do something about this, probably make a new call */
611         /* don't forget there's a hard limit in the interface */
612         fprintf(stderr, "membership list for id %d exceeds display limit\n",
613                 id);
614     }
615     return 0;
616 }
617
618 int
619 pr_GetHostCPS(afs_int32 host, prlist *CPS)
620 {
621     register afs_int32 code;
622     afs_int32 over;
623
624     over = 0;
625     code = ubik_PR_GetHostCPS(pruclient, 0, host, CPS, &over);
626     if (code != PRSUCCESS)
627         return code;
628     if (over) {
629         /* do something about this, probably make a new call */
630         /* don't forget there's a hard limit in the interface */
631         fprintf(stderr,
632                 "membership list for host id %d exceeds display limit\n",
633                 host);
634     }
635     return 0;
636 }
637
638 int
639 pr_ListMembers(char *group, namelist *lnames)
640 {
641     register afs_int32 code;
642     afs_int32 gid;
643
644     code = pr_SNameToId(group, &gid);
645     if (code)
646         return code;
647     if (gid == ANONYMOUSID)
648         return PRNOENT;
649     code = pr_IDListMembers(gid, lnames);
650     return code;
651 }
652
653 int
654 pr_ListOwned(afs_int32 oid, namelist *lnames, afs_int32 *moreP)
655 {
656     register afs_int32 code;
657     prlist alist;
658     idlist *lids;
659
660     alist.prlist_len = 0;
661     alist.prlist_val = 0;
662     code = ubik_PR_ListOwned(pruclient, 0, oid, &alist, moreP);
663     if (code)
664         return code;
665     if (*moreP == 1) {
666         /* Remain backwards compatible when moreP was a T/F bit */
667         fprintf(stderr, "membership list for id %d exceeds display limit\n",
668                 oid);
669         *moreP = 0;
670     }
671     lids = (idlist *) &alist;
672     code = pr_IdToName(lids, lnames);
673
674     xdr_free((xdrproc_t) xdr_prlist, &alist);
675
676     if (code)
677         return code;
678
679     return PRSUCCESS;
680 }
681
682 int
683 pr_IDListMembers(afs_int32 gid, namelist *lnames)
684 {
685     register afs_int32 code;
686     prlist alist;
687     idlist *lids;
688     afs_int32 over;
689
690     alist.prlist_len = 0;
691     alist.prlist_val = 0;
692     code = ubik_PR_ListElements(pruclient, 0, gid, &alist, &over);
693     if (code)
694         return code;
695     if (over) {
696         fprintf(stderr, "membership list for id %d exceeds display limit\n",
697                 gid);
698     }
699     lids = (idlist *) &alist;
700     code = pr_IdToName(lids, lnames);
701
702     xdr_free((xdrproc_t) xdr_prlist, &alist);
703
704     if (code)
705         return code;
706     return PRSUCCESS;
707 }
708
709 int
710 pr_IDListExpandedMembers(afs_int32 aid, namelist * lnames)
711 {
712     afs_int32 code;
713     afs_int32 gid;
714     idlist lids;
715     prlist alist;
716     afs_int32 over;
717     struct idhash *members = NULL;
718     afs_int32 *stack = NULL;
719     afs_int32 maxstack = ID_STACK_SIZE;
720     int n = 0;                  /* number of ids stacked */
721     int i;
722     int firstpass = 1;
723
724     code = AllocateIdHash(&members);
725     if (code) {
726         return code;
727     }
728     stack = (afs_int32 *) malloc(sizeof(afs_int32) * maxstack);
729     if (!stack) {
730         code = ENOMEM;
731         goto done;
732     }
733
734     stack[n++] = aid;
735     while (n) {
736         gid = stack[--n];       /* pop next group id */
737         alist.prlist_len = 0;
738         alist.prlist_val = NULL;
739         if (firstpass || aid < 0) {
740             firstpass = 0;
741             code = ubik_PR_ListElements(pruclient, 0, gid, &alist, &over);
742         } else {
743             code = ubik_PR_ListSuperGroups(pruclient, 0, gid, &alist, &over);
744             if (code == RXGEN_OPCODE) {
745                 alist.prlist_len = 0;
746                 alist.prlist_val = NULL;
747                 code = 0; /* server does not support supergroups. */
748             }
749         }
750         if (code)
751             goto done;
752         if (over) {
753             fprintf(stderr,
754                     "membership list for id %d exceeds display limit\n", gid);
755         }
756         for (i = 0; i < alist.prlist_len; i++) {
757             afs_int32 found;
758             afs_int32 id;
759
760             id = alist.prlist_val[i];
761             found = FindId(members, id);
762             if (found < 0) {
763                 code = found;
764                 xdr_free((xdrproc_t) xdr_prlist, &alist);
765                 goto done;
766             }
767             if (found == 0 && id < 0) {
768                 if (n == maxstack) {    /* need more stack space */
769                     afs_int32 *tmp;
770                     maxstack += n;
771                     tmp =
772                         (afs_int32 *) realloc(stack,
773                                               maxstack * sizeof(afs_int32));
774                     if (!tmp) {
775                         code = ENOMEM;
776                         xdr_free((xdrproc_t) xdr_prlist, &alist);
777                         goto done;
778                     }
779                     stack = tmp;
780                 }
781                 stack[n++] = id;        /* push group id */
782             }
783         }
784         xdr_free((xdrproc_t) xdr_prlist, &alist);
785     }
786
787     code = CreateIdList(members, &lids, (aid < 0 ? PRUSERS : PRGROUPS));
788     if (code) {
789         goto done;
790     }
791     code = pr_IdToName(&lids, lnames);
792     if (lids.idlist_len)
793         free(lids.idlist_val);
794
795   done:
796     if (stack)
797         free(stack);
798     if (members)
799         FreeIdHash(members);
800     return code;
801 }
802
803 int
804 pr_ListEntry(afs_int32 id, struct prcheckentry *aentry)
805 {
806     register afs_int32 code;
807
808     code = ubik_PR_ListEntry(pruclient, 0, id, aentry);
809     return code;
810 }
811
812 afs_int32
813 pr_ListEntries(int flag, afs_int32 startindex, afs_int32 *nentries, struct prlistentries **entries, afs_int32 *nextstartindex)
814 {
815     afs_int32 code;
816     prentries bulkentries;
817
818     *nentries = 0;
819     *entries = NULL;
820     *nextstartindex = -1;
821     bulkentries.prentries_val = 0;
822     bulkentries.prentries_len = 0;
823
824     code =
825         ubik_PR_ListEntries(pruclient, 0, flag, startindex,
826                   &bulkentries, nextstartindex);
827     *nentries = bulkentries.prentries_len;
828     *entries = bulkentries.prentries_val;
829     return code;
830 }
831
832 int
833 pr_CheckEntryByName(char *name, afs_int32 *id, char *owner, char *creator)
834 {
835     /* struct prcheckentry returns other things, which aren't useful to show at this time. */
836     register afs_int32 code;
837     struct prcheckentry aentry;
838
839     code = pr_SNameToId(name, id);
840     if (code)
841         return code;
842     if (*id == ANONYMOUSID)
843         return PRNOENT;
844     code = ubik_PR_ListEntry(pruclient, 0, *id, &aentry);
845     if (code)
846         return code;
847     /* this should be done in one RPC, but I'm lazy. */
848     code = pr_SIdToName(aentry.owner, owner);
849     if (code)
850         return code;
851     code = pr_SIdToName(aentry.creator, creator);
852     if (code)
853         return code;
854     return PRSUCCESS;
855 }
856
857 int
858 pr_CheckEntryById(char *name, afs_int32 id, char *owner, char *creator)
859 {
860     /* struct prcheckentry returns other things, which aren't useful to show at this time. */
861     register afs_int32 code;
862     struct prcheckentry aentry;
863
864     code = pr_SIdToName(id, name);
865     if (code)
866         return code;
867     if (id == ANONYMOUSID)
868         return PRNOENT;
869     code = ubik_PR_ListEntry(pruclient, 0, id, &aentry);
870     if (code)
871         return code;
872     /* this should be done in one RPC, but I'm lazy. */
873     code = pr_SIdToName(aentry.owner, owner);
874     if (code)
875         return code;
876     code = pr_SIdToName(aentry.creator, creator);
877     if (code)
878         return code;
879     return PRSUCCESS;
880 }
881
882 int
883 pr_ChangeEntry(char *oldname, char *newname, afs_int32 *newid, char *newowner)
884 {
885     register afs_int32 code;
886     afs_int32 id;
887     afs_int32 oid = 0;
888
889     code = pr_SNameToId(oldname, &id);
890     if (code)
891         return code;
892     if (id == ANONYMOUSID)
893         return PRNOENT;
894     if (newowner && *newowner) {
895         code = pr_SNameToId(newowner, &oid);
896         if (code)
897             return code;
898         if (oid == ANONYMOUSID)
899             return PRNOENT;
900     }
901     if (newid)
902         code = ubik_PR_ChangeEntry(pruclient, 0, id, newname, oid, *newid);
903     else
904         code = ubik_PR_ChangeEntry(pruclient, 0, id, newname, oid, 0);
905     return code;
906 }
907
908 int
909 pr_IsAMemberOf(char *uname, char *gname, afs_int32 *flag)
910 {
911     register afs_int32 code;
912     namelist lnames;
913     idlist lids;
914
915     stolower(uname);
916     stolower(gname);
917     lnames.namelist_len = 2;
918     lnames.namelist_val = malloc(2 * PR_MAXNAMELEN);
919     strncpy(lnames.namelist_val[0], uname, PR_MAXNAMELEN);
920     strncpy(lnames.namelist_val[1], gname, PR_MAXNAMELEN);
921     lids.idlist_val = 0;
922     lids.idlist_len = 0;
923     code = pr_NameToId(&lnames, &lids);
924     if (code) {
925         if (lnames.namelist_val)
926             free(lnames.namelist_val);
927         xdr_free((xdrproc_t) xdr_idlist, &lids);
928         return code;
929     }
930     code =
931         ubik_PR_IsAMemberOf(pruclient, 0, lids.idlist_val[0],
932                   lids.idlist_val[1], flag);
933     if (lnames.namelist_val)
934         free(lnames.namelist_val);
935     xdr_free((xdrproc_t) xdr_idlist, &lids);
936     return code;
937 }
938
939 int
940 pr_ListMaxUserId(afs_int32 *mid)
941 {
942     register afs_int32 code;
943     afs_int32 gid;
944     code = ubik_PR_ListMax(pruclient, 0, mid, &gid);
945     return code;
946 }
947
948 int
949 pr_SetMaxUserId(afs_int32 mid)
950 {
951     register afs_int32 code;
952     afs_int32 flag = 0;
953     code = ubik_PR_SetMax(pruclient, 0, mid, flag);
954     return code;
955 }
956
957 int
958 pr_ListMaxGroupId(afs_int32 *mid)
959 {
960     register afs_int32 code;
961     afs_int32 id;
962     code = ubik_PR_ListMax(pruclient, 0, &id, mid);
963     return code;
964 }
965
966 int
967 pr_SetMaxGroupId(afs_int32 mid)
968 {
969     register afs_int32 code;
970     afs_int32 flag = 0;
971
972     flag |= PRGRP;
973     code = ubik_PR_SetMax(pruclient, 0, mid, flag);
974     return code;
975 }
976
977 afs_int32
978 pr_SetFieldsEntry(afs_int32 id, afs_int32 mask, afs_int32 flags, afs_int32 ngroups, afs_int32 nusers)
979 {
980     register afs_int32 code;
981
982     code =
983         ubik_PR_SetFieldsEntry(pruclient, 0, id, mask, flags, ngroups,
984                   nusers, 0, 0);
985     return code;
986 }
987
988 int
989 pr_ListSuperGroups(afs_int32 gid, namelist * lnames)
990 {
991     afs_int32 code;
992     prlist alist;
993     idlist *lids;
994     afs_int32 over;
995
996     alist.prlist_len = 0;
997     alist.prlist_val = 0;
998     code = ubik_PR_ListSuperGroups(pruclient, 0, gid, &alist, &over);
999     if (code)
1000         return code;
1001     if (over) {
1002         fprintf(stderr, "supergroup list for id %d exceeds display limit\n",
1003                 gid);
1004     }
1005     lids = (idlist *) & alist;
1006     code = pr_IdToName(lids, lnames);
1007
1008     xdr_free((xdrproc_t) xdr_prlist, &alist);
1009     return code;
1010 }