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