2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
20 #include <afs/cellconfig.h>
21 #include <afs/afsutil.h>
22 #include <afs/com_err.h>
28 struct ubik_client
*pruclient
= 0;
29 static afs_int32 lastLevel
; /* security level pruclient, if any */
31 static char *whoami
= "libprot";
34 #define ID_HASH_SIZE 1024
35 #define ID_STACK_SIZE 1024
38 * Hash table chain of user and group ids.
46 * Hash table of user and group ids.
49 afs_uint32 userEntries
; /**< number of user id entries hashed */
50 afs_uint32 groupEntries
; /**< number of group id entries hashed */
51 struct idchain
*hash
[ID_HASH_SIZE
];
55 * Allocate a new id hash table.
58 AllocateIdHash(struct idhash
**aidhash
)
60 struct idhash
*idhash
;
62 idhash
= calloc(1, sizeof(struct idhash
));
74 FreeIdHash(struct idhash
*idhash
)
77 struct idchain
*chain
;
80 for (index
= 0; index
< ID_HASH_SIZE
; index
++) {
81 for (chain
= idhash
->hash
[index
]; chain
; chain
= next
) {
90 * Indicate if group/user id is already hashed, and
93 * @returns whether id is present
94 * @retval >0 id is already present in the hash
95 * @retval 0 id was not found and was inserted into the hash
96 * @retval <0 error encountered
99 FindId(struct idhash
*idhash
, afs_int32 id
)
102 struct idchain
*chain
;
103 struct idchain
*newChain
;
105 index
= abs(id
) % ID_HASH_SIZE
;
106 for (chain
= idhash
->hash
[index
]; chain
; chain
= chain
->next
) {
107 if (chain
->id
== id
) {
112 /* Insert this id but return not found. */
113 newChain
= malloc(sizeof(struct idchain
));
118 newChain
->next
= idhash
->hash
[index
];
119 idhash
->hash
[index
] = newChain
;
121 idhash
->groupEntries
++;
123 idhash
->userEntries
++;
130 * Create an idlist from the ids in the hash.
133 CreateIdList(struct idhash
*idhash
, idlist
* alist
, afs_int32 select
)
135 struct idchain
*chain
;
136 afs_int32 entries
= 0;
140 if (select
& PRGROUPS
) {
141 entries
+= idhash
->groupEntries
;
143 if (select
& PRUSERS
) {
144 entries
+= idhash
->userEntries
;
147 alist
->idlist_len
= 0;
148 alist
->idlist_val
= NULL
;
152 alist
->idlist_len
= entries
;
153 alist
->idlist_val
= malloc(sizeof(afs_int32
) * entries
);
154 if (!alist
->idlist_val
) {
158 for (i
= 0, index
= 0; index
< ID_HASH_SIZE
; index
++) {
159 for (chain
= idhash
->hash
[index
]; chain
; chain
= chain
->next
) {
161 if (select
& PRGROUPS
) {
162 alist
->idlist_val
[i
++] = chain
->id
;
165 if (select
& PRUSERS
) {
166 alist
->idlist_val
[i
++] = chain
->id
;
175 pr_Initialize(IN afs_int32 secLevel
, IN
const char *confDir
, IN
char *cell
)
178 struct rx_connection
*serverconns
[MAXSERVERS
];
179 struct rx_securityClass
*sc
= NULL
;
180 static struct afsconf_dir
*tdir
= NULL
; /* only do this once */
181 static char tconfDir
[100] = "";
182 static char tcell
[64] = "";
185 static struct afsconf_cell info
;
188 afs_int32 gottdir
= 0;
189 afs_int32 refresh
= 0;
191 initialize_PT_error_table();
192 initialize_RXK_error_table();
193 initialize_ACFG_error_table();
194 initialize_KTC_error_table();
198 tdir
= afsconf_Open(confDir
);
200 if (confDir
&& strcmp(confDir
, ""))
202 "%s: Could not open configuration directory: %s.\n",
206 "%s: No configuration directory specified.\n",
212 code
= afsconf_GetLocalCell(tdir
, cellstr
, sizeof(cellstr
));
215 "libprot: Could not get local cell. [%d]\n", code
);
221 if (tdir
== NULL
|| strcmp(confDir
, tconfDir
) || strcmp(cell
, tcell
)) {
223 * force re-evaluation. we either don't have an afsconf_dir,
224 * the directory has changed or the cell has changed.
226 if (tdir
&& !gottdir
) {
235 strncpy(tconfDir
, confDir
, sizeof(tconfDir
));
236 strncpy(tcell
, cell
, sizeof(tcell
));
239 tdir
= afsconf_Open(confDir
);
241 if (confDir
&& strcmp(confDir
, ""))
243 "libprot: Could not open configuration directory: %s.\n",
247 "libprot: No configuration directory specified.\n");
251 code
= afsconf_GetCellInfo(tdir
, cell
, "afsprot", &info
);
253 fprintf(stderr
, "libprot: Could not locate cell %s in %s/%s\n",
254 cell
, confDir
, AFSDIR_CELLSERVDB_FILE
);
259 /* If we already have a client and it is at the security level we
260 * want, don't get a new one. Unless the security level is 2 in
261 * which case we will get one (and re-read the key file).
263 if (pruclient
&& (lastLevel
== secLevel
) && (secLevel
!= 2)) {
269 fprintf(stderr
, "libprot: Could not initialize rx.\n");
273 /* Most callers use secLevel==1, however, the fileserver uses secLevel==2
274 * to force use of the KeyFile. secLevel == 0 implies -noauth was
277 /* If secLevel is two assume we're on a file server and use
278 * ClientAuthSecure if possible. */
279 code
= afsconf_ClientAuthSecure(tdir
, &sc
, &scIndex
);
281 afs_com_err(whoami
, code
, "(calling client secure)\n");
282 } else if (secLevel
> 0) {
285 secFlags
|= AFSCONF_SECOPTS_ALWAYSENCRYPT
;
287 code
= afsconf_ClientAuthToken(&info
, secFlags
, &sc
, &scIndex
, NULL
);
289 afs_com_err(whoami
, code
, "(getting token)");
296 sc
= rxnull_NewClientSecurityObject();
297 scIndex
= RX_SECIDX_NULL
;
300 if ((scIndex
== RX_SECIDX_NULL
) && (secLevel
!= 0))
302 "%s: Could not get afs tokens, running unauthenticated\n",
305 memset(serverconns
, 0, sizeof(serverconns
)); /* terminate list!!! */
306 for (i
= 0; i
< info
.numServers
; i
++)
308 rx_NewConnection(info
.hostAddr
[i
].sin_addr
.s_addr
,
309 info
.hostAddr
[i
].sin_port
, PRSRV
, sc
,
312 code
= ubik_ClientInit(serverconns
, &pruclient
);
314 afs_com_err(whoami
, code
, "ubik client init failed.");
319 code
= rxs_Release(sc
);
329 code
= ubik_ClientDestroy(pruclient
);
336 * Make sure that arg is a proper C string that fits in a prname.
337 * If strnlen(arg, PR_MAXNAMELEN) == PR_MAXNAMELEN, then arg either
338 * doesn't have a terminating NUL or is too long, and we can't tell
339 * which one in the current API. This code has always assumed that
340 * the names presented to it are valid C strings, but for robustness
341 * we can't depend on the server side guaranteeing that. Unfortunately,
342 * the wire protocol uses a vector[PR_MAXNAMELEN] of char, so XDR will
343 * not automatically fix up strings generated by the server.
345 * The inequality is just belt-and-suspenders and should be impossible.
347 static_inline
int check_length(prname arg
)
349 if (strnlen(arg
, PR_MAXNAMELEN
) >= PR_MAXNAMELEN
)
350 return PRNAMETOOLONG
;
355 pr_CreateUser(prname name
, afs_int32
*id
)
359 code
= check_length(name
);
364 code
= ubik_PR_INewEntry(pruclient
, 0, name
, *id
, 0);
366 code
= ubik_PR_NewEntry(pruclient
, 0, name
, 0, 0, id
);
372 pr_CreateGroup(prname name
, prname owner
, afs_int32
*id
)
378 code
= check_length(name
);
381 /* pr_SNameToId will check owner's length. */
384 code
= pr_SNameToId(owner
, &oid
);
387 if (oid
== ANONYMOUSID
)
392 code
= ubik_PR_INewEntry(pruclient
, 0, name
, *id
, oid
);
394 code
= ubik_PR_NewEntry(pruclient
, 0, name
, flags
, oid
, id
);
400 pr_Delete(prname name
)
405 /* pr_SNameToId both checks the length of name and lowercases it. */
406 code
= pr_SNameToId(name
, &id
);
409 if (id
== ANONYMOUSID
)
411 code
= ubik_PR_Delete(pruclient
, 0, id
);
416 pr_DeleteByID(afs_int32 id
)
420 code
= ubik_PR_Delete(pruclient
, 0, id
);
425 pr_AddToGroup(prname user
, prname group
)
431 code
= check_length(user
);
434 code
= check_length(group
);
437 lnames
.namelist_len
= 2;
438 lnames
.namelist_val
= malloc(2 * PR_MAXNAMELEN
);
439 strncpy(lnames
.namelist_val
[0], user
, PR_MAXNAMELEN
);
440 strncpy(lnames
.namelist_val
[1], group
, PR_MAXNAMELEN
);
443 code
= pr_NameToId(&lnames
, &lids
);
446 /* if here, still could be missing an entry */
447 if (lids
.idlist_len
!= 2) {
451 if (lids
.idlist_val
[0] == ANONYMOUSID
452 || lids
.idlist_val
[1] == ANONYMOUSID
) {
457 ubik_PR_AddToGroup(pruclient
, 0, lids
.idlist_val
[0],
460 if (lnames
.namelist_val
)
461 free(lnames
.namelist_val
);
463 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
468 pr_RemoveUserFromGroup(prname user
, prname group
)
474 code
= check_length(user
);
477 code
= check_length(group
);
480 lnames
.namelist_len
= 2;
481 lnames
.namelist_val
= malloc(2 * PR_MAXNAMELEN
);
482 strncpy(lnames
.namelist_val
[0], user
, PR_MAXNAMELEN
);
483 strncpy(lnames
.namelist_val
[1], group
, PR_MAXNAMELEN
);
486 code
= pr_NameToId(&lnames
, &lids
);
490 if (lids
.idlist_len
!= 2) {
494 if (lids
.idlist_val
[0] == ANONYMOUSID
495 || lids
.idlist_val
[1] == ANONYMOUSID
) {
500 ubik_PR_RemoveFromGroup(pruclient
, 0, lids
.idlist_val
[0],
503 if (lnames
.namelist_val
)
504 free(lnames
.namelist_val
);
506 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
512 pr_NameToId(namelist
*names
, idlist
*ids
)
517 for (i
= 0; i
< names
->namelist_len
; i
++) {
518 code
= check_length(names
->namelist_val
[i
]);
521 stolower(names
->namelist_val
[i
]);
523 code
= ubik_PR_NameToID(pruclient
, 0, names
, ids
);
528 pr_SNameToId(prname name
, afs_int32
*id
)
534 code
= check_length(name
);
539 lnames
.namelist_len
= 1;
540 lnames
.namelist_val
= malloc(PR_MAXNAMELEN
);
542 strncpy(lnames
.namelist_val
[0], name
, PR_MAXNAMELEN
);
543 code
= ubik_PR_NameToID(pruclient
, 0, &lnames
, &lids
);
544 if (lids
.idlist_val
) {
545 *id
= *lids
.idlist_val
;
546 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
547 } else if (code
== 0) {
550 if (lnames
.namelist_val
)
551 free(lnames
.namelist_val
);
556 * Like ubik_PR_IDToName, but enforces that the output prnames are
557 * interpretable as C strings (i.e., NUL-terminated).
560 string_PR_IDToName(struct ubik_client
*client
, afs_int32 flags
,
561 idlist
*ids
, namelist
*names
)
566 code
= ubik_PR_IDToName(client
, flags
, ids
, names
);
569 for (i
= 0; i
< names
->namelist_len
; i
++) {
570 code
= check_length(names
->namelist_val
[i
]);
579 pr_IdToName(idlist
*ids
, namelist
*names
)
581 return string_PR_IDToName(pruclient
, 0, ids
, names
);
585 pr_SIdToName(afs_int32 id
, prname name
)
592 lids
.idlist_val
= malloc(sizeof(afs_int32
));
593 *lids
.idlist_val
= id
;
594 lnames
.namelist_len
= 0;
595 lnames
.namelist_val
= 0;
596 code
= pr_IdToName(&lids
, &lnames
);
597 if (lnames
.namelist_val
)
598 strncpy(name
, lnames
.namelist_val
[0], PR_MAXNAMELEN
);
603 free(lids
.idlist_val
);
605 xdr_free((xdrproc_t
) xdr_namelist
, &lnames
);
611 pr_GetCPS(afs_int32 id
, prlist
*CPS
)
617 code
= ubik_PR_GetCPS(pruclient
, 0, id
, CPS
, &over
);
618 if (code
!= PRSUCCESS
)
621 /* do something about this, probably make a new call */
622 /* don't forget there's a hard limit in the interface */
623 fprintf(stderr
, "membership list for id %d exceeds display limit\n",
630 pr_GetCPS2(afs_int32 id
, afs_uint32 host
, prlist
*CPS
)
636 code
= ubik_PR_GetCPS2(pruclient
, 0, id
, host
, CPS
, &over
);
637 if (code
!= PRSUCCESS
)
640 /* do something about this, probably make a new call */
641 /* don't forget there's a hard limit in the interface */
642 fprintf(stderr
, "membership list for id %d exceeds display limit\n",
649 pr_GetHostCPS(afs_uint32 host
, prlist
*CPS
)
655 code
= ubik_PR_GetHostCPS(pruclient
, 0, host
, CPS
, &over
);
656 if (code
!= PRSUCCESS
)
659 /* do something about this, probably make a new call */
660 /* don't forget there's a hard limit in the interface */
662 "membership list for host id %d exceeds display limit\n",
669 pr_ListMembers(prname group
, namelist
*lnames
)
675 memset(lnames
, 0, sizeof(namelist
));
677 /* pr_SNameToId checks the length of group. */
678 code
= pr_SNameToId(group
, &gid
);
681 if (gid
== ANONYMOUSID
)
683 code
= pr_IDListMembers(gid
, lnames
);
686 for (i
= 0; i
< lnames
->namelist_len
; i
++) {
687 code
= check_length(lnames
->namelist_val
[i
]);
695 pr_ListOwned(afs_int32 oid
, namelist
*lnames
, afs_int32
*moreP
)
701 alist
.prlist_len
= 0;
702 alist
.prlist_val
= 0;
703 code
= ubik_PR_ListOwned(pruclient
, 0, oid
, &alist
, moreP
);
707 /* Remain backwards compatible when moreP was a T/F bit */
708 fprintf(stderr
, "membership list for id %d exceeds display limit\n",
712 lids
= (idlist
*) &alist
;
713 code
= pr_IdToName(lids
, lnames
);
715 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);
724 pr_IDListMembers(afs_int32 gid
, namelist
*lnames
)
731 alist
.prlist_len
= 0;
732 alist
.prlist_val
= 0;
733 code
= ubik_PR_ListElements(pruclient
, 0, gid
, &alist
, &over
);
737 fprintf(stderr
, "membership list for id %d exceeds display limit\n",
740 lids
= (idlist
*) &alist
;
741 code
= pr_IdToName(lids
, lnames
);
743 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);
751 pr_IDListExpandedMembers(afs_int32 aid
, namelist
* lnames
)
758 struct idhash
*members
= NULL
;
759 afs_int32
*stack
= NULL
;
760 afs_int32 maxstack
= ID_STACK_SIZE
;
761 int n
= 0; /* number of ids stacked */
765 code
= AllocateIdHash(&members
);
769 stack
= malloc(sizeof(afs_int32
) * maxstack
);
777 gid
= stack
[--n
]; /* pop next group id */
778 alist
.prlist_len
= 0;
779 alist
.prlist_val
= NULL
;
780 if (firstpass
|| aid
< 0) {
782 code
= ubik_PR_ListElements(pruclient
, 0, gid
, &alist
, &over
);
784 code
= ubik_PR_ListSuperGroups(pruclient
, 0, gid
, &alist
, &over
);
785 if (code
== RXGEN_OPCODE
) {
786 alist
.prlist_len
= 0;
787 alist
.prlist_val
= NULL
;
788 code
= 0; /* server does not support supergroups. */
795 "membership list for id %d exceeds display limit\n", gid
);
797 for (i
= 0; i
< alist
.prlist_len
; i
++) {
801 id
= alist
.prlist_val
[i
];
802 found
= FindId(members
, id
);
805 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);
808 if (found
== 0 && id
< 0) {
809 if (n
== maxstack
) { /* need more stack space */
812 tmp
= realloc(stack
, maxstack
* sizeof(afs_int32
));
815 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);
820 stack
[n
++] = id
; /* push group id */
823 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);
826 code
= CreateIdList(members
, &lids
, (aid
< 0 ? PRUSERS
: PRGROUPS
));
829 } else if (lids
.idlist_len
== 0) {
830 /* Avoid the RPC when there's nothing to look up. */
831 lnames
->namelist_len
= 0;
832 lnames
->namelist_val
= NULL
;
835 code
= pr_IdToName(&lids
, lnames
);
836 free(lids
.idlist_val
);
847 pr_ListEntry(afs_int32 id
, struct prcheckentry
*aentry
)
851 code
= ubik_PR_ListEntry(pruclient
, 0, id
, aentry
);
854 return check_length(aentry
->name
);
858 pr_ListEntries(int flag
, afs_int32 startindex
, afs_int32
*nentries
, struct prlistentries
**entries
, afs_int32
*nextstartindex
)
862 prentries bulkentries
;
866 *nextstartindex
= -1;
867 bulkentries
.prentries_val
= 0;
868 bulkentries
.prentries_len
= 0;
871 ubik_PR_ListEntries(pruclient
, 0, flag
, startindex
,
872 &bulkentries
, nextstartindex
);
875 for (i
= 0; i
< bulkentries
.prentries_len
; i
++) {
876 /* XXX should we try to return all the other entries? */
877 code
= check_length(bulkentries
.prentries_val
[i
].name
);
884 xdr_free((xdrproc_t
)xdr_prentries
, &bulkentries
);
886 *nentries
= bulkentries
.prentries_len
;
887 *entries
= bulkentries
.prentries_val
;
893 pr_CheckEntryByName(prname name
, afs_int32
*id
, prname owner
, prname creator
)
895 /* struct prcheckentry returns other things, which aren't useful to show at this time. */
897 struct prcheckentry aentry
;
899 /* pr_SNameToId will check name's length. */
900 code
= pr_SNameToId(name
, id
);
903 if (*id
== ANONYMOUSID
)
905 code
= ubik_PR_ListEntry(pruclient
, 0, *id
, &aentry
);
908 /* this should be done in one RPC, but I'm lazy. */
909 code
= pr_SIdToName(aentry
.owner
, owner
);
912 code
= pr_SIdToName(aentry
.creator
, creator
);
919 pr_CheckEntryById(prname name
, afs_int32 id
, prname owner
, prname creator
)
921 /* struct prcheckentry returns other things, which aren't useful to show at this time. */
923 struct prcheckentry aentry
;
925 /* XXX ListEntry RPC gives us the name back so should avoid extra RPC */
926 code
= pr_SIdToName(id
, name
);
929 if (id
== ANONYMOUSID
)
931 code
= ubik_PR_ListEntry(pruclient
, 0, id
, &aentry
);
934 /* this should be done in one RPC, but I'm lazy. */
935 code
= pr_SIdToName(aentry
.owner
, owner
);
938 code
= pr_SIdToName(aentry
.creator
, creator
);
945 pr_ChangeEntry(prname oldname
, prname newname
, afs_int32
*newid
, prname newowner
)
951 /* pr_SNameToId takes care of length checks for us. */
952 code
= pr_SNameToId(oldname
, &id
);
955 if (id
== ANONYMOUSID
)
957 if (newowner
&& *newowner
) {
958 code
= pr_SNameToId(newowner
, &oid
);
961 if (oid
== ANONYMOUSID
)
965 code
= ubik_PR_ChangeEntry(pruclient
, 0, id
, newname
, oid
, *newid
);
967 code
= ubik_PR_ChangeEntry(pruclient
, 0, id
, newname
, oid
, 0);
972 pr_IsAMemberOf(prname uname
, prname gname
, afs_int32
*flag
)
978 code
= check_length(uname
);
981 code
= check_length(gname
);
986 lnames
.namelist_len
= 2;
987 lnames
.namelist_val
= malloc(2 * PR_MAXNAMELEN
);
988 strncpy(lnames
.namelist_val
[0], uname
, PR_MAXNAMELEN
);
989 strncpy(lnames
.namelist_val
[1], gname
, PR_MAXNAMELEN
);
992 code
= pr_NameToId(&lnames
, &lids
);
994 if (lnames
.namelist_val
)
995 free(lnames
.namelist_val
);
996 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
999 if (lids
.idlist_len
!= 2) {
1000 free(lnames
.namelist_val
);
1001 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
1005 ubik_PR_IsAMemberOf(pruclient
, 0, lids
.idlist_val
[0],
1006 lids
.idlist_val
[1], flag
);
1007 if (lnames
.namelist_val
)
1008 free(lnames
.namelist_val
);
1009 xdr_free((xdrproc_t
) xdr_idlist
, &lids
);
1014 pr_ListMaxUserId(afs_int32
*mid
)
1018 code
= ubik_PR_ListMax(pruclient
, 0, mid
, &gid
);
1023 pr_SetMaxUserId(afs_int32 mid
)
1027 code
= ubik_PR_SetMax(pruclient
, 0, mid
, flag
);
1032 pr_ListMaxGroupId(afs_int32
*mid
)
1036 code
= ubik_PR_ListMax(pruclient
, 0, &id
, mid
);
1041 pr_SetMaxGroupId(afs_int32 mid
)
1047 code
= ubik_PR_SetMax(pruclient
, 0, mid
, flag
);
1052 pr_SetFieldsEntry(afs_int32 id
, afs_int32 mask
, afs_int32 flags
, afs_int32 ngroups
, afs_int32 nusers
)
1057 ubik_PR_SetFieldsEntry(pruclient
, 0, id
, mask
, flags
, ngroups
,
1063 pr_ListSuperGroups(afs_int32 gid
, namelist
* lnames
)
1070 alist
.prlist_len
= 0;
1071 alist
.prlist_val
= 0;
1072 code
= ubik_PR_ListSuperGroups(pruclient
, 0, gid
, &alist
, &over
);
1076 fprintf(stderr
, "supergroup list for id %d exceeds display limit\n",
1079 lids
= (idlist
*) & alist
;
1080 code
= pr_IdToName(lids
, lnames
);
1082 xdr_free((xdrproc_t
) xdr_prlist
, &alist
);