]>
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, | |
68 | [ZCAP_PTRACE] = CAP_SYS_PTRACE | |
69 | }; | |
70 | ||
71 | static cap_value_t cap_setuid_value [] = { CAP_SETUID }; | |
72 | ||
73 | /* convert zebras privileges to system capabilities */ | |
74 | static cap_value_t * | |
75 | zcaps2sys (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 */ | |
100 | int | |
101 | zprivs_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 | ||
118 | zebra_privs_current_t | |
119 | zprivs_state_caps (void) | |
120 | { | |
121 | int i; | |
01245821 | 122 | cap_flag_value_t val; |
123 | ||
33b72948 | 124 | for (i=0; i < zprivs_state.sys_num_p; i++) |
01245821 | 125 | { |
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) | |
33b72948 | 131 | return ZPRIVS_RAISED; |
01245821 | 132 | } |
133 | return ZPRIVS_LOWERED; | |
134 | } | |
135 | ||
136 | #endif /* HAVE_LCAPS */ | |
137 | ||
138 | int | |
139 | zprivs_change_uid (zebra_privs_ops_t op) | |
140 | { | |
28efaa36 | 141 | |
01245821 | 142 | 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 | ||
150 | zebra_privs_current_t | |
151 | zprivs_state_uid (void) | |
152 | { | |
153 | return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED); | |
154 | } | |
155 | ||
156 | int | |
157 | zprivs_change_null (zebra_privs_ops_t op) | |
158 | { | |
159 | return 0; | |
160 | } | |
161 | ||
162 | zebra_privs_current_t | |
163 | zprivs_state_null (void) | |
164 | { | |
165 | return ZPRIVS_RAISED; | |
166 | } | |
167 | ||
168 | ||
169 | void | |
170 | zprivs_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)) ) | |
28efaa36 | 198 | zprivs_state.zgid = grentry->gr_gid; |
01245821 | 199 | else |
200 | { | |
201 | zlog_err ("privs_init: could not lookup supplied user"); | |
202 | exit (1); | |
203 | } | |
28efaa36 | 204 | |
01245821 | 205 | /* 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 | { | |
33b72948 | 222 | zlog_err("privs_init: could not set PR_SET_KEEPCAPS, %s", |
01245821 | 223 | 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 | { | |
33b72948 | 234 | zlog_err ("privs_init: failed to cap_init, %s", strerror (errno) ); |
01245821 | 235 | exit (1); |
236 | } | |
237 | ||
238 | if ( cap_clear (zprivs_state.caps) ) | |
239 | { | |
33b72948 | 240 | zlog_err ("privs_init: failed to cap_clear, %s", strerror (errno)); |
01245821 | 241 | 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 | ||
28efaa36 | 272 | /* we have caps, we have no need to ever change back the original user */ |
01245821 | 273 | if (zprivs_state.zuid) |
274 | { | |
275 | if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) | |
276 | { | |
33b72948 | 277 | zlog_err ("privs_init (cap): could not setreuid, %s", strerror (errno) ); |
01245821 | 278 | exit (1); |
279 | } | |
28efaa36 | 280 | } |
01245821 | 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 | { | |
33b72948 | 289 | zlog_err ("privs_init: cap_set_proc failed to clear cap_setuid, %s", |
01245821 | 290 | 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 | { | |
33b72948 | 307 | zlog_err ("privs_init (uid): could not setreuid, %s", strerror (errno)); |
01245821 | 308 | exit (1); |
309 | } | |
310 | } | |
311 | ||
312 | zprivs->change = zprivs_change_uid; | |
313 | zprivs->current_state = zprivs_state_uid; | |
314 | #endif /* HAVE_LCAPS */ | |
315 | } | |
316 | ||
317 | void | |
318 | zprivs_terminate (void) | |
319 | { | |
33b72948 | 320 | |
01245821 | 321 | #ifdef HAVE_LCAPS |
33b72948 | 322 | |
323 | if (zprivs_state.caps) | |
324 | cap_clear (zprivs_state.caps); | |
01245821 | 325 | |
326 | if ( cap_set_proc (zprivs_state.caps) ) | |
327 | { | |
33b72948 | 328 | zlog_err ("privs_terminate: cap_set_proc failed, %s", |
01245821 | 329 | strerror (errno) ); |
330 | exit (1); | |
331 | } | |
332 | ||
33b72948 | 333 | if (zprivs_state.sys_num_p) |
01245821 | 334 | XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); |
335 | ||
33b72948 | 336 | if (zprivs_state.sys_num_i) |
01245821 | 337 | 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 | { | |
33b72948 | 345 | zlog_err ("privs_terminate: could not setreuid, %s", |
01245821 | 346 | strerror (errno) ); |
347 | exit (1); | |
348 | } | |
349 | } | |
350 | #endif /* HAVE_LCAPS */ | |
351 | return; | |
352 | } |