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