]>
Commit | Line | Data |
---|---|---|
01245821 | 1 | /* |
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 */ | |
31 | static 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 | |
47 | int zprivs_change_caps (zebra_privs_ops_t); | |
48 | zebra_privs_current_t zprivs_state_caps (void); | |
49 | #endif /* HAVE_LCAPS */ | |
50 | int zprivs_change_uid (zebra_privs_ops_t); | |
51 | zebra_privs_current_t zprivs_state_uid (void); | |
52 | int zprivs_change_null (zebra_privs_ops_t); | |
53 | zebra_privs_current_t zprivs_state_null (void); | |
54 | void zprivs_terminate (void); | |
55 | ||
56 | #ifdef HAVE_LCAPS | |
57 | static int | |
58 | cap_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, | |
8d6b00e4 | 68 | [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 | |
01245821 | 73 | }; |
74 | ||
75 | static cap_value_t cap_setuid_value [] = { CAP_SETUID }; | |
76 | ||
77 | /* convert zebras privileges to system capabilities */ | |
78 | static cap_value_t * | |
79 | zcaps2sys (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 */ | |
104 | int | |
105 | zprivs_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 | ||
122 | zebra_privs_current_t | |
123 | zprivs_state_caps (void) | |
124 | { | |
125 | int i; | |
01245821 | 126 | cap_flag_value_t val; |
127 | ||
33b72948 | 128 | for (i=0; i < zprivs_state.sys_num_p; i++) |
01245821 | 129 | { |
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) | |
33b72948 | 135 | return ZPRIVS_RAISED; |
01245821 | 136 | } |
137 | return ZPRIVS_LOWERED; | |
138 | } | |
139 | ||
140 | #endif /* HAVE_LCAPS */ | |
141 | ||
142 | int | |
143 | zprivs_change_uid (zebra_privs_ops_t op) | |
144 | { | |
28efaa36 | 145 | |
01245821 | 146 | 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 | ||
154 | zebra_privs_current_t | |
155 | zprivs_state_uid (void) | |
156 | { | |
157 | return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED); | |
158 | } | |
159 | ||
160 | int | |
161 | zprivs_change_null (zebra_privs_ops_t op) | |
162 | { | |
163 | return 0; | |
164 | } | |
165 | ||
166 | zebra_privs_current_t | |
167 | zprivs_state_null (void) | |
168 | { | |
169 | return ZPRIVS_RAISED; | |
170 | } | |
171 | ||
172 | ||
173 | void | |
174 | zprivs_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)) ) | |
28efaa36 | 202 | zprivs_state.zgid = grentry->gr_gid; |
01245821 | 203 | else |
204 | { | |
205 | zlog_err ("privs_init: could not lookup supplied user"); | |
206 | exit (1); | |
207 | } | |
28efaa36 | 208 | |
01245821 | 209 | /* 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 | { | |
33b72948 | 226 | zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s", |
01245821 | 227 | 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 | { | |
33b72948 | 238 | zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) ); |
01245821 | 239 | exit (1); |
240 | } | |
241 | ||
242 | if ( cap_clear (zprivs_state.caps) ) | |
243 | { | |
33b72948 | 244 | zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno)); |
01245821 | 245 | 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 | ||
28efaa36 | 276 | /* we have caps, we have no need to ever change back the original user */ |
01245821 | 277 | if (zprivs_state.zuid) |
278 | { | |
279 | if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) | |
280 | { | |
33b72948 | 281 | zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) ); |
01245821 | 282 | exit (1); |
283 | } | |
28efaa36 | 284 | } |
01245821 | 285 | |
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 | { | |
33b72948 | 293 | zlog_err ("privs_init: cap_set_proc failed to clear cap_setuid, %s", |
01245821 | 294 | 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 | { | |
33b72948 | 311 | zlog_err ("privs_init (uid): could not setreuid, %s", strerror (errno)); |
01245821 | 312 | exit (1); |
313 | } | |
314 | } | |
315 | ||
316 | zprivs->change = zprivs_change_uid; | |
317 | zprivs->current_state = zprivs_state_uid; | |
318 | #endif /* HAVE_LCAPS */ | |
319 | } | |
320 | ||
321 | void | |
322 | zprivs_terminate (void) | |
323 | { | |
33b72948 | 324 | |
01245821 | 325 | #ifdef HAVE_LCAPS |
33b72948 | 326 | |
327 | if (zprivs_state.caps) | |
328 | cap_clear (zprivs_state.caps); | |
01245821 | 329 | |
330 | if ( cap_set_proc (zprivs_state.caps) ) | |
331 | { | |
33b72948 | 332 | zlog_err ("privs_terminate: cap_set_proc failed, %s", |
01245821 | 333 | strerror (errno) ); |
334 | exit (1); | |
335 | } | |
336 | ||
33b72948 | 337 | if (zprivs_state.sys_num_p) |
01245821 | 338 | XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); |
339 | ||
33b72948 | 340 | if (zprivs_state.sys_num_i) |
01245821 | 341 | 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 | { | |
33b72948 | 349 | zlog_err ("privs_terminate: could not setreuid, %s", |
01245821 | 350 | strerror (errno) ); |
351 | exit (1); | |
352 | } | |
353 | } | |
354 | #endif /* HAVE_LCAPS */ | |
355 | return; | |
356 | } |