e1b553452114f343b45bd40d535d877874c28c92
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / solaris2 / kopensolaris-gnu / privP.c
1 /* Copyright (C) 2008 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by David Bartley <dtbartle@csclub.uwaterloo.ca>, 2008.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <privP.h>
21 #include <stdarg.h>
22 #include <grp.h>
23 #include <bits/libc-lock.h>
24
25 __libc_lock_define_recursive (extern, __priv_lock);
26 libc_freeres_ptr (static priv_data_t *__data);
27 static priv_set_t *__suidset = NULL;
28
29 priv_data_t * __priv_parse_info (const priv_impl_info_t *pii)
30 {
31   priv_data_t *data = malloc (sizeof (priv_data_t));
32   if (!data)
33     return NULL;
34   memset (data, 0, sizeof (*data));
35   data->pd_setsize = pii->priv_setsize * sizeof (priv_chunk_t);
36
37   /* Iterate over all priv_info_t's. Note that the first priv_info_t follows
38      the header.  */
39   priv_info_t *pi = (priv_info_t *)((char *)pii + pii->priv_headersize);
40   uint32_t left = pii->priv_globalinfosize;
41   while (left)
42     {
43       switch (pi->priv_info_type)
44         {
45         case PRIV_INFO_SETNAMES:
46         case PRIV_INFO_PRIVNAMES:
47           if ((pi->priv_info_type == PRIV_INFO_SETNAMES &&
48                 data->pd_setnames) || (pi->priv_info_type ==
49                 PRIV_INFO_PRIVNAMES && data->pd_privnames))
50             break;
51
52           /* XXX: In priv_get*byname we linearlly scan the list of strins;
53              we could speed this up by sorting the strings here.  */
54
55           priv_info_names_t *pi_names = (priv_info_names_t *)pi;
56
57           /* Allocate memory for the index.  */
58           char **name_list = malloc (sizeof (char *) * pi_names->cnt);
59           if (!name_list)
60             goto err;
61           if (pi->priv_info_type == PRIV_INFO_SETNAMES)
62             {
63               data->pd_setnames = name_list;
64               data->pd_setnames_cnt = pi_names->cnt;
65             }
66           else
67             {
68               data->pd_privnames = name_list;
69               data->pd_privnames_cnt = pi_names->cnt;
70             }
71           data->pd_privnames_cnt = pi_names->cnt;
72
73           /* Setup the index.  */
74           char *names_ptr = pi_names->names;
75           for (int i = 0; i < pi_names->cnt; i++)
76             {
77               name_list[i] = names_ptr;
78               names_ptr += strlen (names_ptr) + 1;
79             }
80
81           break;
82
83         case PRIV_INFO_BASICPRIVS:
84           if (data->pd_basicprivs)
85             break;
86
87           if (pi->priv_info_size != data->pd_setsize)
88             break;
89           data->pd_basicprivs = ((priv_info_set_t *)pi)->set;
90
91           break;
92         }
93
94       left -= pi->priv_info_size;
95       pi = (priv_info_t *)((char *)pi + pi->priv_info_size);
96     }
97
98   return data;
99
100 err:
101
102   free (data->pd_setnames);
103   free (data->pd_privnames);
104   free (data);
105
106   return NULL;
107 }
108
109
110 void __priv_free_info (priv_data_t *data)
111 {
112   free (data->pd_setnames);
113   free (data->pd_privnames);
114   free (data);
115 }
116
117
118 const priv_data_t * __priv_parse_data_cached (void)
119 {
120   if (__data)
121     return __data;
122
123   __libc_lock_lock_recursive (__priv_lock);
124   __data = __priv_parse_info (getprivimplinfo ());
125   __libc_lock_unlock_recursive (__priv_lock);
126
127   return __data;
128 }
129
130
131 /* Specify what privileges an suid root binary needs.  */
132 int __init_suid_priv (int flags, ...)
133 {
134   int res = 0;
135   priv_set_t *permit = NULL, *inherit = NULL, *scratch = NULL;
136
137   /* Check flags.  */
138   if (flags != PU_LIMITPRIVS && flags != PU_CLEARLIMITSET)
139     return -1;
140
141   /* We can only initialize once.  */
142   if (__suidset)
143     return -1;
144
145   /* Do nothing if we are running as root but not setuid root.  */
146   uid_t uid = getuid ();
147   uid_t euid = geteuid ();
148   if (uid == 0 && euid == 0)
149     return 0;
150
151   /* Allocate a scratch set.  */
152   scratch = priv_allocset ();
153   if (!scratch)
154     goto error;
155
156   /* Get the basic set.  */
157   const priv_data_t *pd = __priv_parse_data_cached ();
158   if (!pd)
159     goto error;
160   priv_set_t *basic = pd->pd_basicprivs;
161
162   /* Get the inherited set.  */
163   inherit = priv_allocset ();
164   if (!inherit)
165     goto error;
166   if (getppriv (PRIV_INHERITABLE, inherit) != 0)
167     goto error;
168
169   /* Get the permitted set.  */
170   permit = priv_allocset ();
171   if (!permit)
172     goto error;
173   if (getppriv (PRIV_PERMITTED, permit) != 0)
174     goto error;
175
176   /* Get passed privileges.  */
177   __suidset = priv_allocset ();
178   if (!__suidset)
179     goto error;
180   priv_emptyset (__suidset);
181   va_list ap;
182   va_start (ap, flags);
183   const char *priv;
184   while ((priv = va_arg (ap, const char *)))
185     if (priv_addset (__suidset, priv) != 0)
186       goto error;
187
188   /* Make sure that the passed privileges are a subset of the current
189      permitted privileges.  */
190   if (priv_issubset (__suidset, permit) != B_TRUE)
191     goto error;
192
193   /* Set the effective privileges to the inherited ones.  */
194   if (setppriv (PRIV_SET, PRIV_EFFECTIVE, inherit) != 0)
195     goto error;
196
197   /* Set the permitted privileges to those currently permitted privileges in
198      set of the ones passed in, the inherited ones, and the basic set.  */
199   priv_copyset (__suidset, scratch);
200   priv_union (inherit, scratch);
201   priv_union (basic, scratch);
202   priv_intersect (permit, scratch);
203   if (setppriv (PRIV_SET, PRIV_PERMITTED, scratch) != 0)
204     goto error;
205
206   /* Check if we need to set the limit set.  */
207   if (flags & PU_CLEARLIMITSET)
208     {
209       priv_emptyset (scratch);
210       if (setppriv (PRIV_SET, PRIV_LIMIT, scratch) != 0)
211         goto error;
212     }
213   else if (flags & PU_LIMITPRIVS)
214     {
215       if (setppriv (PRIV_SET, PRIV_LIMIT, scratch) != 0)
216         goto error;
217     }
218
219   /* Change the uid to the caller's uid if we're setuid root.  */
220   if (euid == 0 && setreuid (uid, uid) != 0)
221     goto error;
222
223   goto out;
224
225 error:
226   res = -1;
227   if (__suidset)
228     {
229       priv_freeset (__suidset);
230       __suidset = NULL;
231     }
232   if (euid == 0)
233     setreuid (uid, uid);
234
235 out:
236   priv_freeset (permit);
237   priv_freeset (inherit);
238   priv_freeset (scratch);
239
240   return res;
241 }
242
243
244 // TODO
245 #if 0
246 int __init_daemon_priv (int flags, uid_t uid, gid_t gid, ...)
247 {
248 }
249
250
251 void __fini_daemon_priv (const char *priv, ...)
252 {
253 }
254 #endif
255
256
257 /* Enable or disable those privileges passed in __init_suid_priv.  */
258 int __priv_bracket (priv_op_t op)
259 {
260   if (op != PRIV_ON && op != PRIV_OFF)
261     {
262       __set_errno (EINVAL);
263       return -1;
264     }
265
266   /* We can only toggle privileges if __init_suid_priv was called.  */
267   if (__suidset)
268     return setppriv (op, PRIV_EFFECTIVE, __suidset);
269   else
270     return 0;
271 }
272
273
274 /* Permanently disable those privileges passed in __init_suid_priv.  */
275 void __priv_relinquish (void)
276 {
277   if (__suidset)
278     {
279       setppriv (PRIV_OFF, PRIV_PERMITTED, __suidset);
280       priv_freeset (__suidset);
281       __suidset = NULL;
282     }
283 }