blob: 35e5a76b52e713cafecf7a26b96a7499ad96f2f9 [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,
paul8d6b00e2003-05-22 02:35:52 +000068 [ZCAP_PTRACE] = CAP_SYS_PTRACE,
69 [ZCAP_DAC_OVERRIDE] = CAP_DAC_OVERRIDE,
70 [ZCAP_READ_SEARCH] = CAP_DAC_READ_SEARCH,
71 [ZCAP_SYS_ADMIN] = CAP_SYS_ADMIN,
72 [ZCAP_FOWNER] = ZCAP_FOWNER
paul01245822003-05-20 01:22:17 +000073};
74
75static cap_value_t cap_setuid_value [] = { CAP_SETUID };
76
77/* convert zebras privileges to system capabilities */
78static cap_value_t *
79zcaps2sys (zebra_capabilities_t *zcaps, int num)
80{
81 cap_value_t *syscaps;
82 int i;
83
84 if (!num)
85 return NULL;
86
87 syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS,
88 (sizeof(cap_value_t) * num) );
89 if (!syscaps)
90 {
91 zlog_err ("zcap2sys: could not XCALLOC!");
92 return NULL;
93 }
94
95 for (i=0; i < num; i++)
96 {
97 syscaps[i] = cap_map[zcaps[i]];
98 }
99
100 return syscaps;
101}
102
103/* set or clear the effective capabilities to/from permitted */
104int
105zprivs_change_caps (zebra_privs_ops_t op)
106{
107 cap_flag_value_t cflag;
108
109 if (op == ZPRIVS_RAISE)
110 cflag = CAP_SET;
111 else if (op == ZPRIVS_LOWER)
112 cflag = CAP_CLEAR;
113 else
114 return -1;
115
116 if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
117 zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag))
118 return cap_set_proc (zprivs_state.caps);
119 return -1;
120}
121
122zebra_privs_current_t
123zprivs_state_caps (void)
124{
125 int i;
paul01245822003-05-20 01:22:17 +0000126 cap_flag_value_t val;
127
paul33b72942003-05-20 02:22:42 +0000128 for (i=0; i < zprivs_state.sys_num_p; i++)
paul01245822003-05-20 01:22:17 +0000129 {
130 if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i],
131 CAP_EFFECTIVE, &val) )
132 zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
133 strerror (errno) );
134 if (val == CAP_SET)
paul33b72942003-05-20 02:22:42 +0000135 return ZPRIVS_RAISED;
paul01245822003-05-20 01:22:17 +0000136 }
137 return ZPRIVS_LOWERED;
138}
139
140#endif /* HAVE_LCAPS */
141
142int
143zprivs_change_uid (zebra_privs_ops_t op)
144{
paul28efaa32003-05-20 03:49:43 +0000145
paul01245822003-05-20 01:22:17 +0000146 if (op == ZPRIVS_RAISE)
147 return seteuid (zprivs_state.zsuid);
148 else if (op == ZPRIVS_LOWER)
149 return seteuid (zprivs_state.zuid);
150 else
151 return -1;
152}
153
154zebra_privs_current_t
155zprivs_state_uid (void)
156{
157 return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
158}
159
160int
161zprivs_change_null (zebra_privs_ops_t op)
162{
163 return 0;
164}
165
166zebra_privs_current_t
167zprivs_state_null (void)
168{
169 return ZPRIVS_RAISED;
170}
171
172
173void
174zprivs_init(struct zebra_privs_t *zprivs)
175{
176 struct passwd *pwentry = NULL;
177 struct group *grentry = NULL;
178
179 /* NULL privs */
180 if (! (zprivs->user || zprivs->group
181 || zprivs->cap_num_p || zprivs->cap_num_i) )
182 {
183 zprivs->change = zprivs_change_null;
184 zprivs->current_state = zprivs_state_null;
185 return;
186 }
187
188 if (zprivs->user)
189 {
190 if ( (pwentry = getpwnam (zprivs->user)) )
191 zprivs_state.zuid = pwentry->pw_uid;
192 else
193 {
194 zlog_err ("privs_init: could not lookup supplied user");
195 exit (1);
196 }
197 }
198
199 if (zprivs->group)
200 {
201 if ( (grentry = getgrnam (zprivs->user)) )
paul28efaa32003-05-20 03:49:43 +0000202 zprivs_state.zgid = grentry->gr_gid;
paul01245822003-05-20 01:22:17 +0000203 else
204 {
205 zlog_err ("privs_init: could not lookup supplied user");
206 exit (1);
207 }
paul28efaa32003-05-20 03:49:43 +0000208
paul01245822003-05-20 01:22:17 +0000209 /* change group now, forever. uid we do later */
210 if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
211 {
212 zlog_err ("privs_init: could not setregid");
213 exit (1);
214 }
215 }
216
217#ifdef HAVE_LCAPS
218 zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
219 zprivs_state.sys_num_p = zprivs->cap_num_p;
220 zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
221 zprivs_state.sys_num_i = zprivs->cap_num_i;
222
223 /* Tell kernel we want caps maintained across uid changes */
224 if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
225 {
paul33b72942003-05-20 02:22:42 +0000226 zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s",
paul01245822003-05-20 01:22:17 +0000227 strerror (errno) );
228 exit(1);
229 }
230
231 if ( !zprivs_state.syscaps_p )
232 {
233 zlog_warn ("privs_init: capabilities enabled, but no capabilities supplied");
234 }
235
236 if ( !(zprivs_state.caps = cap_init()) )
237 {
paul33b72942003-05-20 02:22:42 +0000238 zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000239 exit (1);
240 }
241
242 if ( cap_clear (zprivs_state.caps) )
243 {
paul33b72942003-05-20 02:22:42 +0000244 zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno));
paul01245822003-05-20 01:22:17 +0000245 exit (1);
246 }
247
248 /* set permitted caps */
249 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
250 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
251 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
252 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
253
254 /* still need CAP_SETUID for the moment */
255 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
256 1, cap_setuid_value, CAP_SET);
257 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
258 1, cap_setuid_value, CAP_SET);
259
260 /* set inheritable caps, if any */
261 if (zprivs_state.sys_num_i)
262 {
263 cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
264 zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET);
265 }
266
267 /* apply caps. CAP_EFFECTIVE is clear bar cap_setuid_value.
268 * we'll raise the caps as and when, and only when, they are needed.
269 */
270 if ( cap_set_proc (zprivs_state.caps) )
271 {
272 zlog_err ("privs_init: initial cap_set_proc failed");
273 exit (1);
274 }
275
paul28efaa32003-05-20 03:49:43 +0000276 /* we have caps, we have no need to ever change back the original user */
paul01245822003-05-20 01:22:17 +0000277 if (zprivs_state.zuid)
278 {
279 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
280 {
paul33b72942003-05-20 02:22:42 +0000281 zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) );
paul01245822003-05-20 01:22:17 +0000282 exit (1);
283 }
paul28efaa32003-05-20 03:49:43 +0000284 }
paul01245822003-05-20 01:22:17 +0000285
286 /* No more need for cap_setuid_value */
287 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
288 1, cap_setuid_value, CAP_CLEAR);
289 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
290 1, cap_setuid_value, CAP_CLEAR);
291 if ( cap_set_proc (zprivs_state.caps) )
292 {
paul33b72942003-05-20 02:22:42 +0000293 zlog_err ("privs_init: cap_set_proc failed to clear cap_setuid, %s",
paul01245822003-05-20 01:22:17 +0000294 strerror (errno) );
295 exit (1);
296 }
297
298 zprivs->change = zprivs_change_caps;
299 zprivs->current_state = zprivs_state_caps;
300
301#elif !defined(HAVE_LCAPS)
302 /* we dont have caps. we'll need to maintain rid and saved uid
303 * and change euid back to saved uid (who we presume has all neccessary
304 * privileges) whenever we are asked to raise our privileges.
305 */
306 zprivs_state.zsuid = geteuid();
307 if ( zprivs_state.zuid )
308 {
309 if ( setreuid (-1, zprivs_state.zuid) )
310 {
paul33b72942003-05-20 02:22:42 +0000311 zlog_err ("privs_init (uid): could not setreuid, %s", strerror (errno));
paul01245822003-05-20 01:22:17 +0000312 exit (1);
313 }
314 }
315
316 zprivs->change = zprivs_change_uid;
317 zprivs->current_state = zprivs_state_uid;
318#endif /* HAVE_LCAPS */
319}
320
321void
322zprivs_terminate (void)
323{
paul33b72942003-05-20 02:22:42 +0000324
paul01245822003-05-20 01:22:17 +0000325#ifdef HAVE_LCAPS
paul33b72942003-05-20 02:22:42 +0000326
327 if (zprivs_state.caps)
328 cap_clear (zprivs_state.caps);
paul01245822003-05-20 01:22:17 +0000329
330 if ( cap_set_proc (zprivs_state.caps) )
331 {
paul33b72942003-05-20 02:22:42 +0000332 zlog_err ("privs_terminate: cap_set_proc failed, %s",
paul01245822003-05-20 01:22:17 +0000333 strerror (errno) );
334 exit (1);
335 }
336
paul33b72942003-05-20 02:22:42 +0000337 if (zprivs_state.sys_num_p)
paul01245822003-05-20 01:22:17 +0000338 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
339
paul33b72942003-05-20 02:22:42 +0000340 if (zprivs_state.sys_num_i)
paul01245822003-05-20 01:22:17 +0000341 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
342
343 cap_free (zprivs_state.caps);
344#else
345 if (zprivs_state.zuid)
346 {
347 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
348 {
paul33b72942003-05-20 02:22:42 +0000349 zlog_err ("privs_terminate: could not setreuid, %s",
paul01245822003-05-20 01:22:17 +0000350 strerror (errno) );
351 exit (1);
352 }
353 }
354#endif /* HAVE_LCAPS */
355 return;
356}