amd64-update-20040202
[openafs.git] / src / afs / LINUX / 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  * setgroups (syscall)
13  * setpag
14  *
15  */
16 #include <afsconfig.h>
17 #include "afs/param.h"
18
19 RCSID
20     ("$Header$");
21
22 #include "afs/sysincludes.h"
23 #include "afsincludes.h"
24 #include "afs/afs_stats.h"      /* statistics */
25 #ifdef AFS_LINUX22_ENV
26 #include "h/smp_lock.h"
27 #endif
28
29 static int afs_getgroups(cred_t * cr, gid_t * groups);
30 static int afs_setgroups(cred_t ** cr, int ngroups, gid_t * gidset,
31                          int change_parent);
32
33 /* Only propogate the PAG to the parent process. Unix's propogate to 
34  * all processes sharing the cred.
35  */
36 int
37 set_pag_in_parent(int pag, int g0, int g1)
38 {
39 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
40     gid_t *gp = current->parent->groups;
41 #else
42     gid_t *gp = current->p_pptr->groups;
43 #endif
44     int ngroups;
45     int i;
46
47
48 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
49     ngroups = current->parent->ngroups;
50 #else
51     ngroups = current->p_pptr->ngroups;
52 #endif
53
54     if ((ngroups < 2) || (afs_get_pag_from_groups(gp[0], gp[1]) == NOPAG)) {
55         /* We will have to shift grouplist to make room for pag */
56         if (ngroups + 2 > NGROUPS) {
57             return EINVAL;
58         }
59         for (i = ngroups - 1; i >= 0; i--) {
60             gp[i + 2] = gp[i];
61         }
62         ngroups += 2;
63     }
64     gp[0] = g0;
65     gp[1] = g1;
66     if (ngroups < NGROUPS)
67         gp[ngroups] = NOGROUP;
68
69 #ifdef STRUCT_TASK_STRUCT_HAS_PARENT
70     current->parent->ngroups = ngroups;
71 #else
72     current->p_pptr->ngroups = ngroups;
73 #endif
74     return 0;
75 }
76
77 int
78 setpag(cred_t ** cr, afs_uint32 pagvalue, afs_uint32 * newpag,
79        int change_parent)
80 {
81     gid_t *gidset;
82     afs_int32 ngroups, code = 0;
83     int j;
84
85     AFS_STATCNT(setpag);
86
87     gidset = (gid_t *) osi_Alloc(NGROUPS * sizeof(gidset[0]));
88     ngroups = afs_getgroups(*cr, gidset);
89
90     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
91         /* We will have to shift grouplist to make room for pag */
92         if (ngroups + 2 > NGROUPS) {
93             osi_Free((char *)gidset, NGROUPS * sizeof(int));
94             return EINVAL;
95         }
96         for (j = ngroups - 1; j >= 0; j--) {
97             gidset[j + 2] = gidset[j];
98         }
99         ngroups += 2;
100     }
101     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
102     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
103     code = afs_setgroups(cr, ngroups, gidset, change_parent);
104
105     /* If change_parent is set, then we should set the pag in the parent as
106      * well.
107      */
108     if (change_parent && !code) {
109         code = set_pag_in_parent(*newpag, gidset[0], gidset[1]);
110     }
111
112     osi_Free((char *)gidset, NGROUPS * sizeof(int));
113     return code;
114 }
115
116
117 /* Intercept the standard system call. */
118 extern long (*sys_setgroupsp) (int gidsetsize, gid_t * grouplist);
119 asmlinkage long
120 afs_xsetgroups(int gidsetsize, gid_t * grouplist)
121 {
122     long code;
123     cred_t *cr = crref();
124     afs_uint32 junk;
125     int old_pag;
126
127     lock_kernel();
128     old_pag = PagInCred(cr);
129     crfree(cr);
130     unlock_kernel();
131
132     code = (*sys_setgroupsp) (gidsetsize, grouplist);
133     if (code) {
134         return code;
135     }
136
137     lock_kernel();
138     cr = crref();
139     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
140         /* re-install old pag if there's room. */
141         code = setpag(&cr, old_pag, &junk, 0);
142     }
143     crfree(cr);
144     unlock_kernel();
145
146     /* Linux syscall ABI returns errno as negative */
147     return (-code);
148 }
149
150 #if defined(AFS_LINUX24_ENV)
151 /* Intercept the standard uid32 system call. */
152 extern long (*sys_setgroups32p) (int gidsetsize, gid_t * grouplist);
153 asmlinkage long
154 afs_xsetgroups32(int gidsetsize, gid_t * grouplist)
155 {
156     long code;
157     cred_t *cr = crref();
158     afs_uint32 junk;
159     int old_pag;
160
161     lock_kernel();
162     old_pag = PagInCred(cr);
163     crfree(cr);
164     unlock_kernel();
165
166     code = (*sys_setgroups32p) (gidsetsize, grouplist);
167
168     if (code) {
169         return code;
170     }
171
172     lock_kernel();
173     cr = crref();
174     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
175         /* re-install old pag if there's room. */
176         code = setpag(&cr, old_pag, &junk, 0);
177     }
178     crfree(cr);
179     unlock_kernel();
180
181     /* Linux syscall ABI returns errno as negative */
182     return (-code);
183 }
184 #endif
185
186 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_AMD64_LINUX20_ENV)
187 /* Intercept the uid16 system call as used by 32bit programs. */
188 extern long (*sys32_setgroupsp) (int gidsetsize, u16 * grouplist);
189 asmlinkage long
190 afs32_xsetgroups(int gidsetsize, u16 * grouplist)
191 {
192     long code;
193     cred_t *cr = crref();
194     afs_uint32 junk;
195     int old_pag;
196
197     lock_kernel();
198     old_pag = PagInCred(cr);
199     crfree(cr);
200     unlock_kernel();
201
202     code = (*sys32_setgroupsp) (gidsetsize, grouplist);
203     if (code) {
204         return code;
205     }
206
207     lock_kernel();
208     cr = crref();
209     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
210         /* re-install old pag if there's room. */
211         code = setpag(&cr, old_pag, &junk, 0);
212     }
213     crfree(cr);
214     unlock_kernel();
215
216     /* Linux syscall ABI returns errno as negative */
217     return (-code);
218 }
219
220 #ifdef AFS_LINUX24_ENV
221 /* Intercept the uid32 system call as used by 32bit programs. */
222 extern long (*sys32_setgroups32p) (int gidsetsize, gid_t * grouplist);
223 asmlinkage long
224 afs32_xsetgroups32(int gidsetsize, gid_t * grouplist)
225 {
226     long code;
227     cred_t *cr = crref();
228     afs_uint32 junk;
229     int old_pag;
230
231     lock_kernel();
232     old_pag = PagInCred(cr);
233     crfree(cr);
234     unlock_kernel();
235
236     code = (*sys32_setgroups32p) (gidsetsize, grouplist);
237     if (code) {
238         return code;
239     }
240
241     lock_kernel();
242     cr = crref();
243     if (old_pag != NOPAG && PagInCred(cr) == NOPAG) {
244         /* re-install old pag if there's room. */
245         code = setpag(&cr, old_pag, &junk, 0);
246     }
247     crfree(cr);
248     unlock_kernel();
249
250     /* Linux syscall ABI returns errno as negative */
251     return (-code);
252 }
253 #endif
254 #endif
255
256 static int
257 afs_setgroups(cred_t ** cr, int ngroups, gid_t * gidset, int change_parent)
258 {
259     int ngrps;
260     int i;
261     gid_t *gp;
262
263     AFS_STATCNT(afs_setgroups);
264
265     if (ngroups > NGROUPS)
266         return EINVAL;
267
268     gp = (*cr)->cr_groups;
269     if (ngroups < NGROUPS)
270         gp[ngroups] = (gid_t) NOGROUP;
271
272     for (i = ngroups; i > 0; i--) {
273         *gp++ = *gidset++;
274     }
275
276     (*cr)->cr_ngroups = ngroups;
277     crset(*cr);
278     return (0);
279 }
280
281 /* Returns number of groups. And we trust groups to be large enough to
282  * hold all the groups.
283  */
284 static int
285 afs_getgroups(cred_t * cr, gid_t * groups)
286 {
287     int i;
288     gid_t *gp = cr->cr_groups;
289     int n = cr->cr_ngroups;
290     AFS_STATCNT(afs_getgroups);
291
292     for (i = 0; (i < n) && (*gp != (gid_t) NOGROUP); i++) {
293         *groups++ = *gp++;
294     }
295     return i;
296 }