Commit | Line | Data |
---|---|---|
805e021f CE |
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 | /* | |
11 | * Implements: | |
12 | * setgroups (syscall) | |
13 | * setpag | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <afsconfig.h> | |
18 | #include "afs/param.h" | |
19 | ||
20 | #include <unistd.h> | |
21 | #ifdef AFS_SUN510_ENV | |
22 | #include <sys/cred.h> | |
23 | #endif | |
24 | ||
25 | ||
26 | #include "afs/sysincludes.h" | |
27 | #include "afsincludes.h" | |
28 | #include "afs/afs_stats.h" /* statistics */ | |
29 | ||
30 | ||
31 | static int | |
32 | afs_getgroups(struct cred *cred, gid_t * gidset); | |
33 | ||
34 | static int | |
35 | afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset, | |
36 | int change_parent); | |
37 | ||
38 | ||
39 | int | |
40 | afs_xsetgroups(uap, rvp) | |
41 | u_int uap; /* this is gidsetsize */ | |
42 | gid_t *rvp; /* this is gidset */ | |
43 | { | |
44 | int code = 0; | |
45 | struct vrequest treq; | |
46 | struct proc *proc = ttoproc(curthread); | |
47 | ||
48 | AFS_STATCNT(afs_xsetgroups); | |
49 | AFS_GLOCK(); | |
50 | code = afs_InitReq(&treq, proc->p_cred); | |
51 | AFS_GUNLOCK(); | |
52 | if (code) | |
53 | return code; | |
54 | code = setgroups(uap, rvp); | |
55 | ||
56 | /* Note that if there is a pag already in the new groups we don't | |
57 | * overwrite it with the old pag. | |
58 | */ | |
59 | if (PagInCred(proc->p_cred) == NOPAG) { | |
60 | if (((treq.uid >> 24) & 0xff) == 'A') { | |
61 | AFS_GLOCK(); | |
62 | /* we've already done a setpag, so now we redo it */ | |
63 | AddPag(treq.uid, &proc->p_cred); | |
64 | AFS_GUNLOCK(); | |
65 | } | |
66 | } | |
67 | return code; | |
68 | } | |
69 | ||
70 | #ifdef AFS_PAG_ONEGROUP_ENV | |
71 | /** | |
72 | * Take a PAG, and put it into the given array of gids. | |
73 | * | |
74 | * @param[in] pagvalue The numeric id for the PAG to assign (must not be -1) | |
75 | * @param[in] gidset An array of gids | |
76 | * @param[inout] a_ngroups How many entries in 'gidset' have valid gids | |
77 | * @param[in] gidset_sz The number of bytes allocated for 'gidset' | |
78 | * | |
79 | * @return error code | |
80 | */ | |
81 | static int | |
82 | pag_to_gidset(afs_uint32 pagvalue, gid_t *gidset, int *a_ngroups, | |
83 | size_t gidset_sz) | |
84 | { | |
85 | int i; | |
86 | gid_t *gidslot = NULL; | |
87 | int ngroups = *a_ngroups; | |
88 | ||
89 | osi_Assert(pagvalue != -1); | |
90 | ||
91 | /* See if we already have a PAG gid */ | |
92 | for (i = 0; i < ngroups; i++) { | |
93 | if (((gidset[i] >> 24) & 0xff) == 'A') { | |
94 | gidslot = &gidset[i]; | |
95 | break; | |
96 | } | |
97 | } | |
98 | ||
99 | if (gidslot == NULL) { | |
100 | /* If we don't already have a PAG, grow the groups list by one, and put | |
101 | * our PAG in the new empty slot. */ | |
102 | if ((sizeof(gidset[0])) * (ngroups + 1) > gidset_sz) { | |
103 | return E2BIG; | |
104 | } | |
105 | ngroups += 1; | |
106 | gidslot = &gidset[ngroups-1]; | |
107 | } | |
108 | ||
109 | /* | |
110 | * For newer Solaris releases (Solaris 11), we cannot control the order of | |
111 | * the supplemental groups list of a process, so we can't store PAG gids as | |
112 | * the first two gids anymore. To make finding a PAG gid easier to find, | |
113 | * just use a single gid to represent a PAG, and just store the PAG id | |
114 | * itself in there, like is currently done on Linux. Note that our PAG ids | |
115 | * all start with the byte 0x41 ('A'), so we should not collide with | |
116 | * anything. GIDs with the highest bit set are special (used for Windows | |
117 | * SID mapping), but anything under that range should be fine. | |
118 | */ | |
119 | *gidslot = pagvalue; | |
120 | ||
121 | *a_ngroups = ngroups; | |
122 | ||
123 | return 0; | |
124 | } | |
125 | #else | |
126 | /* For earlier Solaris releases, convert a PAG number into two gids, and store | |
127 | * those gids as the first groups in the supplemental group list. */ | |
128 | static int | |
129 | pag_to_gidset(afs_uint32 pagvalue, gid_t *gidset, int *a_ngroups, | |
130 | size_t gidset_sz) | |
131 | { | |
132 | int j; | |
133 | int ngroups = *a_ngroups; | |
134 | ||
135 | osi_Assert(pagvalue != -1); | |
136 | ||
137 | if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) { | |
138 | /* We will have to shift grouplist to make room for pag */ | |
139 | if ((sizeof(gidset[0])) * (ngroups + 2) > gidset_sz) { | |
140 | return E2BIG; | |
141 | } | |
142 | for (j = ngroups - 1; j >= 0; j--) { | |
143 | gidset[j + 2] = gidset[j]; | |
144 | } | |
145 | ngroups += 2; | |
146 | } | |
147 | afs_get_groups_from_pag(pagvalue, &gidset[0], &gidset[1]); | |
148 | ||
149 | *a_ngroups = ngroups; | |
150 | ||
151 | return 0; | |
152 | } | |
153 | #endif | |
154 | ||
155 | int | |
156 | setpag(cred, pagvalue, newpag, change_parent) | |
157 | struct cred **cred; | |
158 | afs_uint32 pagvalue; | |
159 | afs_uint32 *newpag; | |
160 | afs_uint32 change_parent; | |
161 | { | |
162 | gid_t *gidset; | |
163 | int ngroups, code; | |
164 | size_t gidset_sz; | |
165 | ||
166 | AFS_STATCNT(setpag); | |
167 | ||
168 | /* Derive gidset size from running kernel's ngroups_max; | |
169 | * default 16, but configurable up to 32 (Sol10) or | |
170 | * 1024 (Sol11). | |
171 | */ | |
172 | gidset_sz = sizeof(gidset[0]) * ngroups_max; | |
173 | ||
174 | /* must use osi_Alloc, osi_AllocSmallSpace may not be enough. */ | |
175 | gidset = osi_Alloc(gidset_sz); | |
176 | ||
177 | pagvalue = (pagvalue == -1 ? genpag() : pagvalue); | |
178 | ||
179 | mutex_enter(&curproc->p_crlock); | |
180 | ngroups = afs_getgroups(*cred, gidset); | |
181 | ||
182 | code = pag_to_gidset(pagvalue, gidset, &ngroups, gidset_sz); | |
183 | if (code != 0) { | |
184 | mutex_exit(&curproc->p_crlock); | |
185 | goto done; | |
186 | } | |
187 | ||
188 | *newpag = pagvalue; | |
189 | ||
190 | /* afs_setgroups will release curproc->p_crlock */ | |
191 | /* exit action is same regardless of code */ | |
192 | code = afs_setgroups(cred, ngroups, gidset, change_parent); | |
193 | ||
194 | done: | |
195 | osi_Free((char *)gidset, gidset_sz); | |
196 | return code; | |
197 | } | |
198 | ||
199 | ||
200 | static int | |
201 | afs_getgroups(struct cred *cred, gid_t * gidset) | |
202 | { | |
203 | int ngrps, savengrps; | |
204 | gid_t *gp; | |
205 | ||
206 | AFS_STATCNT(afs_getgroups); | |
207 | ||
208 | gidset[0] = gidset[1] = 0; | |
209 | #if defined(AFS_SUN510_ENV) | |
210 | savengrps = ngrps = crgetngroups(cred); | |
211 | gp = crgetgroups(cred); | |
212 | #else | |
213 | savengrps = ngrps = cred->cr_ngroups; | |
214 | gp = cred->cr_groups; | |
215 | #endif | |
216 | while (ngrps--) | |
217 | *gidset++ = *gp++; | |
218 | return savengrps; | |
219 | } | |
220 | ||
221 | ||
222 | ||
223 | static int | |
224 | afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset, | |
225 | int change_parent) | |
226 | { | |
227 | #ifndef AFS_PAG_ONEGROUP_ENV | |
228 | gid_t *gp; | |
229 | #endif | |
230 | ||
231 | AFS_STATCNT(afs_setgroups); | |
232 | ||
233 | if (ngroups > ngroups_max) { | |
234 | mutex_exit(&curproc->p_crlock); | |
235 | return EINVAL; | |
236 | } | |
237 | if (!change_parent) | |
238 | *cred = (struct cred *)crcopy(*cred); | |
239 | ||
240 | #ifdef AFS_PAG_ONEGROUP_ENV | |
241 | crsetgroups(*cred, ngroups, gidset); | |
242 | #else | |
243 | # if defined(AFS_SUN510_ENV) | |
244 | crsetgroups(*cred, ngroups, gidset); | |
245 | gp = crgetgroups(*cred); | |
246 | # else | |
247 | (*cred)->cr_ngroups = ngroups; | |
248 | gp = (*cred)->cr_groups; | |
249 | # endif | |
250 | while (ngroups--) | |
251 | *gp++ = *gidset++; | |
252 | #endif /* !AFS_PAG_ONEGROUP_ENV */ | |
253 | ||
254 | mutex_exit(&curproc->p_crlock); | |
255 | if (!change_parent) | |
256 | crset(curproc, *cred); /* broadcast to all threads */ | |
257 | return (0); | |
258 | } | |
259 | ||
260 | #ifdef AFS_PAG_ONEGROUP_ENV | |
261 | afs_int32 | |
262 | osi_get_group_pag(struct cred *cred) { | |
263 | gid_t *gidset; | |
264 | int ngroups; | |
265 | int i; | |
266 | ||
267 | gidset = crgetgroups(cred); | |
268 | ngroups = crgetngroups(cred); | |
269 | ||
270 | for (i = 0; i < ngroups; i++) { | |
271 | if (((gidset[i] >> 24) & 0xff) == 'A') { | |
272 | return gidset[i]; | |
273 | } | |
274 | } | |
275 | return NOPAG; | |
276 | } | |
277 | #endif |