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