blob: 1902310dfd737b5cd4ea21607eb1794c2f65b1f2 [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{
paul28efaa32003-05-20 03:49:43 +0000141
paul01245822003-05-20 01:22:17 +0000142 if (op == ZPRIVS_RAISE)
143 return seteuid (zprivs_state.zsuid);
144 else if (op == ZPRIVS_LOWER)
145 return seteuid (zprivs_state.zuid);
146 else
147 return -1;
148}
149
150zebra_privs_current_t
151zprivs_state_uid (void)
152{
153 return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
154}
155
156int
157zprivs_change_null (zebra_privs_ops_t op)
158{
159 return 0;
160}
161
162zebra_privs_current_t
163zprivs_state_null (void)
164{
165 return ZPRIVS_RAISED;
166}
167
168
169void
170zprivs_init(struct zebra_privs_t *zprivs)
171{
172 struct passwd *pwentry = NULL;
173 struct group *grentry = NULL;
174
175 /* NULL privs */
176 if (! (zprivs->user || zprivs->group
177 || zprivs->cap_num_p || zprivs->cap_num_i) )
178 {
179 zprivs->change = zprivs_change_null;
180 zprivs->current_state = zprivs_state_null;
181 return;
182 }
183
184 if (zprivs->user)
185 {
186 if ( (pwentry = getpwnam (zprivs->user)) )
187 zprivs_state.zuid = pwentry->pw_uid;
188 else
189 {
190 zlog_err ("privs_init: could not lookup supplied user");
191 exit (1);
192 }
193 }
194
195 if (zprivs->group)
196 {
197 if ( (grentry = getgrnam (zprivs->user)) )
paul28efaa32003-05-20 03:49:43 +0000198 zprivs_state.zgid = grentry->gr_gid;
paul01245822003-05-20 01:22:17 +0000199 else
200 {
201 zlog_err ("privs_init: could not lookup supplied user");
202 exit (1);
203 }
paul28efaa32003-05-20 03:49:43 +0000204
paul01245822003-05-20 01:22:17 +0000205 /* change group now, forever. uid we do later */
206 if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
207 {
208 zlog_err ("privs_init: could not setregid");
209 exit (1);
210 }
211 }
212
213#ifdef HAVE_LCAPS
214 zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
215 zprivs_state.sys_num_p = zprivs->cap_num_p;
216 zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
217 zprivs_state.sys_num_i = zprivs->cap_num_i;
218
219 /* Tell kernel we want caps maintained across uid changes */
220 if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
221 {
paul33b72942003-05-20 02:22:42 +0000222 zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s",
paul01245822003-05-20 01:22:17 +0000223 strerror (errno) );
224 exit(1);
225 }
226
227 if ( !zprivs_state.syscaps_p )
228 {
229 zlog_warn ("privs_init: capabilities enabled, but no capabilities supplied");
230 }
231
232 if ( !(zprivs_state.caps = cap_init()) )
233 {
paul33b72942003-05-20 02:22:42 +0000234 zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000235 exit (1);
236 }
237
238 if ( cap_clear (zprivs_state.caps) )
239 {
paul33b72942003-05-20 02:22:42 +0000240 zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno));
paul01245822003-05-20 01:22:17 +0000241 exit (1);
242 }
243
244 /* set permitted caps */
245 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
246 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
247 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
248 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
249
250 /* still need CAP_SETUID for the moment */
251 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
252 1, cap_setuid_value, CAP_SET);
253 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
254 1, cap_setuid_value, CAP_SET);
255
256 /* set inheritable caps, if any */
257 if (zprivs_state.sys_num_i)
258 {
259 cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
260 zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET);
261 }
262
263 /* apply caps. CAP_EFFECTIVE is clear bar cap_setuid_value.
264 * we'll raise the caps as and when, and only when, they are needed.
265 */
266 if ( cap_set_proc (zprivs_state.caps) )
267 {
268 zlog_err ("privs_init: initial cap_set_proc failed");
269 exit (1);
270 }
271
paul28efaa32003-05-20 03:49:43 +0000272 /* we have caps, we have no need to ever change back the original user */
paul01245822003-05-20 01:22:17 +0000273 if (zprivs_state.zuid)
274 {
275 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
276 {
paul33b72942003-05-20 02:22:42 +0000277 zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000278 exit (1);
279 }
paul28efaa32003-05-20 03:49:43 +0000280 }
paul01245822003-05-20 01:22:17 +0000281
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}