6b6cc80554b8f1f1dcb34b77445ebd2d0d687f70
[openafs.git] / src / afs / IRIX / osi_groups.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 /*
11  * Implements:
12  * afsDFS_SetPagInCred (shared with SGI)
13  * osi_DFSGetPagFromCred (shared with SGI)
14  * Afs_xsetgroups (syscall)
15  * setpag
16  *
17  */
18 #include <afsconfig.h>
19 #include "afs/param.h"
20
21 RCSID
22     ("$Header$");
23
24 #include "afs/sysincludes.h"
25 #include "afsincludes.h"
26 #include "afs/afs_stats.h"      /* statistics */
27
28
29 static int
30   afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset);
31
32 static int
33   afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset,
34                 int change_parent);
35
36
37 /* This is common code between SGI's DFS and our AFS. Do *not* alter it's
38  * interface or semantics without notifying SGI.
39  */
40 #ifdef AFS_SGI65_ENV
41 /* fixup_pags returns error code if relevant or 0 on no error.
42  * Sets up the cred for the call to estgroups. This is pretty convoluted
43  * in order to avoid including the private proc.h header file.
44  */
45 int
46 fixup_pags(int **credpp, int ngroups, gid_t * gidset, int old_afs_pag,
47            int old_dfs_pag)
48 {
49     int new_afs_pag = 0;
50     int new_dfs_pag = 0;
51     int new, old;
52     int changed = 0;
53     cred_t *cr;
54     gid_t groups[NGROUPS_UMAX];
55     int code;
56
57     if (ngroups < 0 || ngroups > ngroups_max)
58         return EINVAL;
59
60     if (ngroups) {
61         AFS_COPYIN(gidset, groups, ngroups * sizeof(gid_t), code);
62         if (code)
63             return EFAULT;
64     }
65
66     if (ngroups >= 2) {         /* possibly an AFS PAG */
67         new_afs_pag =
68             (afs_get_pag_from_groups(groups[0], groups[1]) != NOPAG);
69     }
70     if (ngroups >= 1) {         /* possibly a DFS PAG */
71         new_dfs_pag = (int)groups[ngroups - 1];
72         if (((new_dfs_pag >> 24) & 0xff) == 'A')
73             new_dfs_pag = (int)groups[ngroups - 1];
74         else
75             new_dfs_pag = 0;
76     }
77
78     /* Now compute the number of groups we will need. */
79     new = ngroups;
80     if (old_afs_pag && !new_afs_pag)    /* prepend old AFS pag */
81         new += 2;
82     if (old_dfs_pag && !new_dfs_pag)    /* append old DFS pag */
83         new++;
84
85     if (new > ngroups_max)
86         return EINVAL;          /* sorry */
87
88     cr = crdup(OSI_GET_CURRENT_CRED()); /* we will replace all the groups. */
89     memset((char *)&cr->cr_groups, 0, ngroups_max * sizeof(gid_t));
90
91     /* Now cobble the new groups list together. */
92     new = 0;
93     old = 0;
94     if (old_afs_pag && !new_afs_pag) {  /* prepend old AFS pag */
95         gid_t g0, g1;
96         changed = 1;
97         afs_get_groups_from_pag(old_afs_pag, &g0, &g1);
98         cr->cr_groups[new++] = g0;
99         cr->cr_groups[new++] = g1;
100     }
101
102     for (old = 0; old < ngroups; old++)
103         cr->cr_groups[new++] = groups[old];
104
105     if (old_dfs_pag && !new_dfs_pag) {  /* append old DFS pag */
106         changed = 1;
107         cr->cr_groups[new++] = old_dfs_pag;
108     }
109
110     /* Now, did we do anything? */
111     if (changed) {
112         cr->cr_ngroups = new;
113         *credpp = cr;
114     } else {
115         crfree(cr);
116         *credpp = NULL;
117     }
118     return 0;
119 }
120 #else
121 /*
122  * Generic routine to set the PAG in the cred for AFS and DFS.
123  * If flag = 0 this is a DFS pag held in one group.
124  * If flag = 1 this is a AFS pag held in two group entries
125  */
126 static int
127 afsDFS_SetPagInCred(struct ucred *credp, int pag, int flag)
128 {
129     int *gidset;
130     int i, ngrps;
131     gid_t g0, g1;
132     int n = 0;
133     struct ucred *newcredp;
134     int groups_taken = (flag ? 2 : 1);
135
136     ngrps = credp->cr_ngroups + groups_taken;
137     if (ngrps >= ngroups_max)
138         return E2BIG;
139
140
141     if (flag) {
142         /* Break out the AFS pag into two groups */
143         afs_get_groups_from_pag(pag, &g0, &g1);
144     }
145
146     newcredp = crdup(credp);
147     newcredp->cr_ngroups = ngrps;
148
149     if (flag) {
150         /* AFS case */
151         newcredp->cr_groups[0] = g0;
152         newcredp->cr_groups[1] = g1;
153     } else {
154         /* DFS case */
155         if (PagInCred(newcredp) != NOPAG) {
156             /* found an AFS PAG is set in this cred */
157             n = 2;
158         }
159         newcredp->cr_groups[n] = pag;
160     }
161     for (i = n; i < credp->cr_ngroups; i++)
162         newcredp->cr_groups[i + groups_taken] = credp->cr_groups[i];
163
164     /* estgroups sets current threads cred from newcredp and crfree's credp */
165     estgroups(credp, newcredp);
166
167     return 0;
168 }
169 #endif /* AFS_SGI65_ENV */
170
171 /* SGI's osi_GetPagFromCred - They return a long. */
172 int
173 osi_DFSGetPagFromCred(struct ucred *credp)
174 {
175     int pag;
176     int ngroups;
177
178     /*
179      *  For IRIX, the PAG is stored in the first entry
180      *  of the gruop list in the cred structure.  gid_t's
181      *  are 32 bits on 64 bit and 32 bit hardware types.
182      *  As of Irix 6.5, the DFS pag is the last group in the list.
183      */
184     ngroups = credp->cr_ngroups;
185     if (ngroups < 1)
186         return NOPAG;
187     /*
188      *  Keep in mind that we might be living with AFS here.
189      *  This means we don't really know if our DFS PAG is in
190      *  the first or third group entry.
191      */
192 #ifdef AFS_SGI65_ENV
193     pag = credp->cr_groups[ngroups - 1];
194 #else
195     pag = credp->cr_groups[0];
196     if (PagInCred(credp) != NOPAG) {
197         /* AFS has a PAG value in the first two group entries */
198         if (ngroups < 3)
199             return NOPAG;
200         pag = credp->cr_groups[2];
201     }
202 #endif
203     if (((pag >> 24) & 0xff) == 'A')
204         return pag;
205     else
206         return NOPAG;
207 }
208
209 int
210 Afs_xsetgroups(int ngroups, gid_t * gidset)
211 {
212     int old_afs_pag = NOPAG;
213     int old_dfs_pag = NOPAG;
214     int code = 0;
215     struct ucred *credp = OSI_GET_CURRENT_CRED();
216     struct ucred *modcredp;
217
218
219     credp = OSI_GET_CURRENT_CRED();
220     /* First get any old PAG's */
221     old_afs_pag = PagInCred(credp);
222     old_dfs_pag = osi_DFSGetPagFromCred(credp);
223
224     /* Set the passed in group list. */
225     if (code = setgroups(ngroups, gidset))
226         return code;
227
228 #ifdef AFS_SGI65_ENV
229     if (old_afs_pag == NOPAG && old_dfs_pag == NOPAG)
230         return 0;
231
232     /* Well, we could get the cred, except it's in the proc struct which
233      * is not a publicly available header. And the cred won't be valid on
234      * the uthread until we return to user space. So, we examine the passed
235      * in groups in fixup_pags.
236      */
237     code =
238         fixup_pags(&modcredp, ngroups, gidset,
239                    (old_afs_pag == NOPAG) ? 0 : old_afs_pag,
240                    (old_dfs_pag == NOPAG) ? 0 : old_dfs_pag);
241     if (!code && modcredp)
242         estgroups(OSI_GET_CURRENT_PROCP(), modcredp);
243 #else
244
245     /*
246      * The setgroups gave our curent thread a new cred pointer
247      * Get the value again
248      */
249     credp = OSI_GET_CURRENT_CRED();
250     if ((PagInCred(credp) == NOPAG) && (old_afs_pag != NOPAG)) {
251         /* reset the AFS PAG */
252         code = afsDFS_SetPagInCred(credp, old_afs_pag, 1);
253     }
254     /*
255      * Once again get the credp because the afsDFS_SetPagInCred might have
256      * assigned a new one.
257      */
258     credp = OSI_GET_CURRENT_CRED();
259     if ((osi_DFSGetPagFromCred(credp) == NOPAG)
260         && (old_dfs_pag != NOPAG)) {
261         code = afsDFS_SetPagInCred(credp, old_dfs_pag, 0);
262     }
263 #endif /* AFS_SGI65_ENV */
264     return code;
265 }
266
267
268 int
269 setpag(cred, pagvalue, newpag, change_parent)
270      struct ucred **cred;
271      afs_uint32 pagvalue;
272      afs_uint32 *newpag;
273      afs_uint32 change_parent;
274 {
275     gid_t gidset[NGROUPS];
276     int ngroups, code;
277     int j;
278
279     AFS_STATCNT(setpag);
280
281     ngroups = afs_getgroups(*cred, NGROUPS, gidset);
282     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
283         /* We will have to shift grouplist to make room for pag */
284         if (ngroups + 2 > NGROUPS) {
285 #if defined(KERNEL_HAVE_UERROR)
286             return (setuerror(E2BIG), E2BIG);
287 #else
288             return (E2BIG);
289 #endif
290         }
291         for (j = ngroups - 1; j >= 0; j--) {
292             gidset[j + 2] = gidset[j];
293         }
294         ngroups += 2;
295     }
296     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
297     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
298     if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {
299 #if defined(KERNEL_HAVE_UERROR)
300         return (setuerror(code), code);
301 #else
302         return code;
303 #endif
304     }
305     return code;
306 }
307
308
309 static int
310 afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset)
311 {
312     int ngrps, savengrps;
313     gid_t *gp;
314
315     gidset[0] = gidset[1] = 0;
316     AFS_STATCNT(afs_getgroups);
317     savengrps = ngrps = MIN(ngroups, cred->cr_ngroups);
318     gp = cred->cr_groups;
319     while (ngrps--)
320         *gidset++ = *gp++;
321     return savengrps;
322 }
323
324
325
326 static int
327 afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset,
328               int change_parent)
329 {
330     gid_t *gp;
331     cred_t *cr, *newcr;
332
333     AFS_STATCNT(afs_setgroups);
334
335     if (ngroups > ngroups_max)
336         return EINVAL;
337     cr = *cred;
338     if (!change_parent)
339         newcr = crdup(cr);
340     else
341         newcr = cr;
342     newcr->cr_ngroups = ngroups;
343     gp = newcr->cr_groups;
344     while (ngroups--)
345         *gp++ = *gidset++;
346     if (!change_parent) {
347 #ifdef AFS_SGI65_ENV
348         estgroups(OSI_GET_CURRENT_PROCP(), newcr);
349 #else
350         estgroups(cr, newcr);
351 #endif
352     }
353     *cred = newcr;
354     return (0);
355 }