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