b7cbe1f792da3f9c17ac37b13bcf542a08ef784a
[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 (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 int (*sys_setgroupsp)(int gidsetsize, gid_t *grouplist);
104 asmlinkage int 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 #if defined(AFS_SPARC64_LINUX20_ENV)
166 asmlinkage int afs32_xsetgroups(int gidsetsize, __kernel_gid_t32 *grouplist)
167 {
168     gid_t gl[NGROUPS];
169     int ret, i;
170     mm_segment_t old_fs = get_fs ();
171
172     if ((unsigned) gidsetsize > NGROUPS)
173         return -EINVAL;
174     for (i = 0; i < gidsetsize; i++, grouplist++)
175         if (__get_user (gl[i], grouplist))
176             return -EFAULT;
177     set_fs (KERNEL_DS);
178     ret = afs_xsetgroups(gidsetsize, gl);
179     set_fs (old_fs);
180     return ret;
181 }
182 #ifdef AFS_LINUX24_ENV
183 asmlinkage int afs32_xsetgroups32(int gidsetsize, __kernel_gid_t32 *grouplist)
184 {
185     gid_t gl[NGROUPS];
186     int ret, i;
187     mm_segment_t old_fs = get_fs ();
188
189     if ((unsigned) gidsetsize > NGROUPS)
190         return -EINVAL;
191     for (i = 0; i < gidsetsize; i++, grouplist++)
192         if (__get_user (gl[i], grouplist))
193             return -EFAULT;
194     set_fs (KERNEL_DS);
195     ret = afs_xsetgroups32(gidsetsize, gl);
196     set_fs (old_fs);
197     return ret;
198 }
199 #endif
200 #endif
201
202 static int afs_setgroups(cred_t **cr, int ngroups, gid_t *gidset, int change_parent)
203 {
204     int ngrps;
205     int i;
206     gid_t *gp;
207
208     AFS_STATCNT(afs_setgroups);
209
210     if (ngroups > NGROUPS)
211         return EINVAL;
212
213     gp = (*cr)->cr_groups;
214     if (ngroups < NGROUPS)
215         gp[ngroups] = (gid_t)NOGROUP;
216
217     for (i = ngroups; i > 0; i--) {
218         *gp++ = *gidset++;
219     }
220
221     (*cr)->cr_ngroups = ngroups;
222     crset(*cr);
223     return (0);
224 }
225
226 /* Returns number of groups. And we trust groups to be large enough to
227  * hold all the groups.
228  */
229 static int afs_getgroups(cred_t *cr, gid_t *groups)
230 {
231     int i;
232     gid_t *gp = cr->cr_groups;
233     int n = cr->cr_ngroups;
234     AFS_STATCNT(afs_getgroups);
235
236     for (i = 0; (i < n) && (*gp != (gid_t)NOGROUP); i++) {
237         *groups++ = *gp++;
238     }
239     return i;
240 }
241