blob: 0ee8d60d722738e19136b181bda5a17e5a002da4 [file] [log] [blame]
paul01245822003-05-20 01:22:17 +00001/*
2 * Zebra privileges.
3 *
4 * Copyright (C) 2003 Paul Jakma.
5 *
6 * This file is part of GNU Zebra.
7 *
8 * GNU Zebra is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Zebra; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24#include <zebra.h>
25#include "log.h"
26#include "privs.h"
27#include "memory.h"
28
29
30/* internal privileges state */
31static struct _zprivs_t
32{
33#ifdef HAVE_LCAPS
34 cap_t caps; /* caps storage */
35 cap_value_t *syscaps_p; /* system permitted caps */
36 cap_value_t *syscaps_i; /* system inheritable caps */
37 int sys_num_p; /* number of syscaps_p */
38 int sys_num_i; /* number of syscaps_i */
39#endif /* HAVE_LCAPS */
40 uid_t zuid, /* uid to run as */
41 zsuid; /* saved uid */
42 gid_t zgid; /* gid to run as */
43} zprivs_state;
44
45/* externally exported but not directly accessed functions */
46#ifdef HAVE_LCAPS
47int zprivs_change_caps (zebra_privs_ops_t);
48zebra_privs_current_t zprivs_state_caps (void);
49#endif /* HAVE_LCAPS */
50int zprivs_change_uid (zebra_privs_ops_t);
51zebra_privs_current_t zprivs_state_uid (void);
52int zprivs_change_null (zebra_privs_ops_t);
53zebra_privs_current_t zprivs_state_null (void);
54void zprivs_terminate (void);
55
56#ifdef HAVE_LCAPS
57static int
58cap_map [ZCAP_MAX] =
59{
60 [ZCAP_SETGID] = CAP_SETGID,
61 [ZCAP_SETUID] = CAP_SETUID,
62 [ZCAP_BIND] = CAP_NET_BIND_SERVICE,
63 [ZCAP_BROADCAST] = CAP_NET_BROADCAST,
64 [ZCAP_ADMIN] = CAP_NET_ADMIN,
65 [ZCAP_RAW] = CAP_NET_RAW,
66 [ZCAP_CHROOT] = CAP_SYS_CHROOT,
67 [ZCAP_NICE] = CAP_SYS_NICE,
68 [ZCAP_PTRACE] = CAP_SYS_PTRACE
69};
70
71static cap_value_t cap_setuid_value [] = { CAP_SETUID };
72
73/* convert zebras privileges to system capabilities */
74static cap_value_t *
75zcaps2sys (zebra_capabilities_t *zcaps, int num)
76{
77 cap_value_t *syscaps;
78 int i;
79
80 if (!num)
81 return NULL;
82
83 syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS,
84 (sizeof(cap_value_t) * num) );
85 if (!syscaps)
86 {
87 zlog_err ("zcap2sys: could not XCALLOC!");
88 return NULL;
89 }
90
91 for (i=0; i < num; i++)
92 {
93 syscaps[i] = cap_map[zcaps[i]];
94 }
95
96 return syscaps;
97}
98
99/* set or clear the effective capabilities to/from permitted */
100int
101zprivs_change_caps (zebra_privs_ops_t op)
102{
103 cap_flag_value_t cflag;
104
105 if (op == ZPRIVS_RAISE)
106 cflag = CAP_SET;
107 else if (op == ZPRIVS_LOWER)
108 cflag = CAP_CLEAR;
109 else
110 return -1;
111
112 if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
113 zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag))
114 return cap_set_proc (zprivs_state.caps);
115 return -1;
116}
117
118zebra_privs_current_t
119zprivs_state_caps (void)
120{
121 int i;
paul01245822003-05-20 01:22:17 +0000122 cap_flag_value_t val;
123
paul33b72942003-05-20 02:22:42 +0000124 for (i=0; i < zprivs_state.sys_num_p; i++)
paul01245822003-05-20 01:22:17 +0000125 {
126 if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i],
127 CAP_EFFECTIVE, &val) )
128 zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
129 strerror (errno) );
130 if (val == CAP_SET)
paul33b72942003-05-20 02:22:42 +0000131 return ZPRIVS_RAISED;
paul01245822003-05-20 01:22:17 +0000132 }
133 return ZPRIVS_LOWERED;
134}
135
136#endif /* HAVE_LCAPS */
137
138int
139zprivs_change_uid (zebra_privs_ops_t op)
140{
141 if (op == ZPRIVS_RAISE)
142 return seteuid (zprivs_state.zsuid);
143 else if (op == ZPRIVS_LOWER)
144 return seteuid (zprivs_state.zuid);
145 else
146 return -1;
147}
148
149zebra_privs_current_t
150zprivs_state_uid (void)
151{
152 return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
153}
154
155int
156zprivs_change_null (zebra_privs_ops_t op)
157{
158 return 0;
159}
160
161zebra_privs_current_t
162zprivs_state_null (void)
163{
164 return ZPRIVS_RAISED;
165}
166
167
168void
169zprivs_init(struct zebra_privs_t *zprivs)
170{
171 struct passwd *pwentry = NULL;
172 struct group *grentry = NULL;
173
174 /* NULL privs */
175 if (! (zprivs->user || zprivs->group
176 || zprivs->cap_num_p || zprivs->cap_num_i) )
177 {
178 zprivs->change = zprivs_change_null;
179 zprivs->current_state = zprivs_state_null;
180 return;
181 }
182
183 if (zprivs->user)
184 {
185 if ( (pwentry = getpwnam (zprivs->user)) )
186 zprivs_state.zuid = pwentry->pw_uid;
187 else
188 {
189 zlog_err ("privs_init: could not lookup supplied user");
190 exit (1);
191 }
192 }
193
194 if (zprivs->group)
195 {
196 if ( (grentry = getgrnam (zprivs->user)) )
197 zprivs_state.zgid = pwentry->pw_uid;
198 else
199 {
200 zlog_err ("privs_init: could not lookup supplied user");
201 exit (1);
202 }
203
204 /* change group now, forever. uid we do later */
205 if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
206 {
207 zlog_err ("privs_init: could not setregid");
208 exit (1);
209 }
210 }
211
212#ifdef HAVE_LCAPS
213 zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
214 zprivs_state.sys_num_p = zprivs->cap_num_p;
215 zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
216 zprivs_state.sys_num_i = zprivs->cap_num_i;
217
218 /* Tell kernel we want caps maintained across uid changes */
219 if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
220 {
paul33b72942003-05-20 02:22:42 +0000221 zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s",
paul01245822003-05-20 01:22:17 +0000222 strerror (errno) );
223 exit(1);
224 }
225
226 if ( !zprivs_state.syscaps_p )
227 {
228 zlog_warn ("privs_init: capabilities enabled, but no capabilities supplied");
229 }
230
231 if ( !(zprivs_state.caps = cap_init()) )
232 {
paul33b72942003-05-20 02:22:42 +0000233 zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000234 exit (1);
235 }
236
237 if ( cap_clear (zprivs_state.caps) )
238 {
paul33b72942003-05-20 02:22:42 +0000239 zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno));
paul01245822003-05-20 01:22:17 +0000240 exit (1);
241 }
242
243 /* set permitted caps */
244 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
245 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
246 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
247 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
248
249 /* still need CAP_SETUID for the moment */
250 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
251 1, cap_setuid_value, CAP_SET);
252 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
253 1, cap_setuid_value, CAP_SET);
254
255 /* set inheritable caps, if any */
256 if (zprivs_state.sys_num_i)
257 {
258 cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
259 zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET);
260 }
261
262 /* apply caps. CAP_EFFECTIVE is clear bar cap_setuid_value.
263 * we'll raise the caps as and when, and only when, they are needed.
264 */
265 if ( cap_set_proc (zprivs_state.caps) )
266 {
267 zlog_err ("privs_init: initial cap_set_proc failed");
268 exit (1);
269 }
270
271 /* we have caps, we have no need to ever change back the original user
272 if (zprivs_state.zuid)
273 {
274 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
275 {
paul33b72942003-05-20 02:22:42 +0000276 zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000277 exit (1);
278 }
279 }
280 */
281
282 /* No more need for cap_setuid_value */
283 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
284 1, cap_setuid_value, CAP_CLEAR);
285 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
286 1, cap_setuid_value, CAP_CLEAR);
287 if ( cap_set_proc (zprivs_state.caps) )
288 {
paul33b72942003-05-20 02:22:42 +0000289 zlog_err ("privs_init: cap_set_proc failed to clear cap_setuid, %s",
paul01245822003-05-20 01:22:17 +0000290 strerror (errno) );
291 exit (1);
292 }
293
294 zprivs->change = zprivs_change_caps;
295 zprivs->current_state = zprivs_state_caps;
296
297#elif !defined(HAVE_LCAPS)
298 /* we dont have caps. we'll need to maintain rid and saved uid
299 * and change euid back to saved uid (who we presume has all neccessary
300 * privileges) whenever we are asked to raise our privileges.
301 */
302 zprivs_state.zsuid = geteuid();
303 if ( zprivs_state.zuid )
304 {
305 if ( setreuid (-1, zprivs_state.zuid) )
306 {
paul33b72942003-05-20 02:22:42 +0000307 zlog_err ("privs_init (uid): could not setreuid, %s", strerror (errno));
paul01245822003-05-20 01:22:17 +0000308 exit (1);
309 }
310 }
311
312 zprivs->change = zprivs_change_uid;
313 zprivs->current_state = zprivs_state_uid;
314#endif /* HAVE_LCAPS */
315}
316
317void
318zprivs_terminate (void)
319{
paul33b72942003-05-20 02:22:42 +0000320
paul01245822003-05-20 01:22:17 +0000321#ifdef HAVE_LCAPS
paul33b72942003-05-20 02:22:42 +0000322
323 if (zprivs_state.caps)
324 cap_clear (zprivs_state.caps);
paul01245822003-05-20 01:22:17 +0000325
326 if ( cap_set_proc (zprivs_state.caps) )
327 {
paul33b72942003-05-20 02:22:42 +0000328 zlog_err ("privs_terminate: cap_set_proc failed, %s",
paul01245822003-05-20 01:22:17 +0000329 strerror (errno) );
330 exit (1);
331 }
332
paul33b72942003-05-20 02:22:42 +0000333 if (zprivs_state.sys_num_p)
paul01245822003-05-20 01:22:17 +0000334 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
335
paul33b72942003-05-20 02:22:42 +0000336 if (zprivs_state.sys_num_i)
paul01245822003-05-20 01:22:17 +0000337 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
338
339 cap_free (zprivs_state.caps);
340#else
341 if (zprivs_state.zuid)
342 {
343 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
344 {
paul33b72942003-05-20 02:22:42 +0000345 zlog_err ("privs_terminate: could not setreuid, %s",
paul01245822003-05-20 01:22:17 +0000346 strerror (errno) );
347 exit (1);
348 }
349 }
350#endif /* HAVE_LCAPS */
351 return;
352}