Solaris: setpag should verify that ngroups will not overflow
[openafs.git] / src / afs / SOLARIS / 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
17 #include <afsconfig.h>
18 #include "afs/param.h"
19
20 #include <unistd.h>
21 #ifdef AFS_SUN510_ENV
22 #include <sys/cred.h>
23 #endif
24
25
26 #include "afs/sysincludes.h"
27 #include "afsincludes.h"
28 #include "afs/afs_stats.h"      /* statistics */
29
30
31 static int
32   afs_getgroups(struct cred *cred, gid_t * gidset);
33
34 static int
35   afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset,
36                 int change_parent);
37
38
39 int
40 afs_xsetgroups(uap, rvp)
41      u_int uap;                 /* this is gidsetsize */
42      gid_t *rvp;                /* this is gidset */
43 {
44     int code = 0;
45     struct vrequest treq;
46     struct proc *proc = ttoproc(curthread);
47
48     AFS_STATCNT(afs_xsetgroups);
49     AFS_GLOCK();
50     code = afs_InitReq(&treq, proc->p_cred);
51     AFS_GUNLOCK();
52     if (code)
53         return code;
54     code = setgroups(uap, rvp);
55
56     /* Note that if there is a pag already in the new groups we don't
57      * overwrite it with the old pag.
58      */
59     if (PagInCred(proc->p_cred) == NOPAG) {
60         if (((treq.uid >> 24) & 0xff) == 'A') {
61             AFS_GLOCK();
62             /* we've already done a setpag, so now we redo it */
63             AddPag(treq.uid, &proc->p_cred);
64             AFS_GUNLOCK();
65         }
66     }
67     return code;
68 }
69
70 int
71 setpag(cred, pagvalue, newpag, change_parent)
72      struct cred **cred;
73      afs_uint32 pagvalue;
74      afs_uint32 *newpag;
75      afs_uint32 change_parent;
76 {
77     gid_t *gidset;
78     int ngroups, code;
79     int j;
80     size_t gidset_sz;
81
82     AFS_STATCNT(setpag);
83
84     /* Derive gidset size from running kernel's ngroups_max;
85      * default 16, but configurable up to 32 (Sol10) or
86      * 1024 (Sol11).
87      */
88     gidset_sz = sizeof(gidset[0]) * ngroups_max;
89
90     /* must use osi_Alloc, osi_AllocSmallSpace may not be enough. */
91     gidset = osi_Alloc(gidset_sz);
92
93     mutex_enter(&curproc->p_crlock);
94     ngroups = afs_getgroups(*cred, gidset);
95
96     if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
97         /* We will have to shift grouplist to make room for pag */
98         if ((sizeof gidset[0]) * (ngroups + 2) > gidset_sz) {
99             mutex_exit(&curproc->p_crlock);
100             code = E2BIG;
101             goto done;
102         }
103         for (j = ngroups - 1; j >= 0; j--) {
104             gidset[j + 2] = gidset[j];
105         }
106         ngroups += 2;
107     }
108     *newpag = (pagvalue == -1 ? genpag() : pagvalue);
109     afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
110     /* afs_setgroups will release curproc->p_crlock */
111     /* exit action is same regardless of code */
112     code = afs_setgroups(cred, ngroups, gidset, change_parent);
113
114  done:
115     osi_Free((char *)gidset, gidset_sz);
116     return code;
117 }
118
119
120 static int
121 afs_getgroups(struct cred *cred, gid_t * gidset)
122 {
123     int ngrps, savengrps;
124     gid_t *gp;
125
126     AFS_STATCNT(afs_getgroups);
127
128     gidset[0] = gidset[1] = 0;
129 #if defined(AFS_SUN510_ENV)
130     savengrps = ngrps = crgetngroups(cred);
131     gp = crgetgroups(cred);
132 #else
133     savengrps = ngrps = cred->cr_ngroups;
134     gp = cred->cr_groups;
135 #endif
136     while (ngrps--)
137         *gidset++ = *gp++;
138     return savengrps;
139 }
140
141
142
143 static int
144 afs_setgroups(struct cred **cred, int ngroups, gid_t * gidset,
145               int change_parent)
146 {
147     gid_t *gp;
148
149     AFS_STATCNT(afs_setgroups);
150
151     if (ngroups > ngroups_max) {
152         mutex_exit(&curproc->p_crlock);
153         return EINVAL;
154     }
155     if (!change_parent)
156         *cred = (struct cred *)crcopy(*cred);
157 #if defined(AFS_SUN510_ENV)
158     crsetgroups(*cred, ngroups, gidset);
159     gp = crgetgroups(*cred);
160 #else
161     (*cred)->cr_ngroups = ngroups;
162     gp = (*cred)->cr_groups;
163 #endif
164     while (ngroups--)
165         *gp++ = *gidset++;
166     mutex_exit(&curproc->p_crlock);
167     if (!change_parent)
168         crset(curproc, *cred);  /* broadcast to all threads */
169     return (0);
170 }