]>
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" | |
01245821 | 28 | |
29 | /* internal privileges state */ | |
30 | static struct _zprivs_t | |
31 | { | |
32 | #ifdef HAVE_LCAPS | |
33 | cap_t caps; /* caps storage */ | |
34 | cap_value_t *syscaps_p; /* system permitted caps */ | |
35 | cap_value_t *syscaps_i; /* system inheritable caps */ | |
36 | int sys_num_p; /* number of syscaps_p */ | |
37 | int sys_num_i; /* number of syscaps_i */ | |
38 | #endif /* HAVE_LCAPS */ | |
39 | uid_t zuid, /* uid to run as */ | |
40 | zsuid; /* saved uid */ | |
41 | gid_t zgid; /* gid to run as */ | |
ba3a0bc5 | 42 | gid_t vtygrp; /* gid for vty sockets */ |
01245821 | 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, | |
ba3a0bc5 | 72 | [ZCAP_FOWNER] = CAP_FOWNER |
01245821 | 73 | }; |
74 | ||
01245821 | 75 | /* convert zebras privileges to system capabilities */ |
76 | static cap_value_t * | |
77 | zcaps2sys (zebra_capabilities_t *zcaps, int num) | |
78 | { | |
79 | cap_value_t *syscaps; | |
80 | int i; | |
81 | ||
82 | if (!num) | |
83 | return NULL; | |
84 | ||
85 | syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS, | |
86 | (sizeof(cap_value_t) * num) ); | |
87 | if (!syscaps) | |
88 | { | |
89 | zlog_err ("zcap2sys: could not XCALLOC!"); | |
90 | return NULL; | |
91 | } | |
92 | ||
93 | for (i=0; i < num; i++) | |
94 | { | |
95 | syscaps[i] = cap_map[zcaps[i]]; | |
96 | } | |
97 | ||
98 | return syscaps; | |
99 | } | |
100 | ||
101 | /* set or clear the effective capabilities to/from permitted */ | |
102 | int | |
103 | zprivs_change_caps (zebra_privs_ops_t op) | |
104 | { | |
105 | cap_flag_value_t cflag; | |
106 | ||
107 | if (op == ZPRIVS_RAISE) | |
108 | cflag = CAP_SET; | |
109 | else if (op == ZPRIVS_LOWER) | |
110 | cflag = CAP_CLEAR; | |
111 | else | |
112 | return -1; | |
113 | ||
114 | if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE, | |
115 | zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag)) | |
116 | return cap_set_proc (zprivs_state.caps); | |
117 | return -1; | |
118 | } | |
119 | ||
120 | zebra_privs_current_t | |
121 | zprivs_state_caps (void) | |
122 | { | |
123 | int i; | |
01245821 | 124 | cap_flag_value_t val; |
125 | ||
33b72948 | 126 | for (i=0; i < zprivs_state.sys_num_p; i++) |
01245821 | 127 | { |
128 | if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i], | |
129 | CAP_EFFECTIVE, &val) ) | |
130 | zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s", | |
131 | strerror (errno) ); | |
132 | if (val == CAP_SET) | |
33b72948 | 133 | return ZPRIVS_RAISED; |
01245821 | 134 | } |
135 | return ZPRIVS_LOWERED; | |
136 | } | |
137 | ||
138 | #endif /* HAVE_LCAPS */ | |
139 | ||
140 | int | |
141 | zprivs_change_uid (zebra_privs_ops_t op) | |
142 | { | |
28efaa36 | 143 | |
01245821 | 144 | if (op == ZPRIVS_RAISE) |
145 | return seteuid (zprivs_state.zsuid); | |
146 | else if (op == ZPRIVS_LOWER) | |
147 | return seteuid (zprivs_state.zuid); | |
148 | else | |
149 | return -1; | |
150 | } | |
151 | ||
152 | zebra_privs_current_t | |
153 | zprivs_state_uid (void) | |
154 | { | |
155 | return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED); | |
156 | } | |
157 | ||
158 | int | |
159 | zprivs_change_null (zebra_privs_ops_t op) | |
160 | { | |
161 | return 0; | |
162 | } | |
163 | ||
164 | zebra_privs_current_t | |
165 | zprivs_state_null (void) | |
166 | { | |
167 | return ZPRIVS_RAISED; | |
168 | } | |
169 | ||
170 | ||
171 | void | |
172 | zprivs_init(struct zebra_privs_t *zprivs) | |
173 | { | |
174 | struct passwd *pwentry = NULL; | |
175 | struct group *grentry = NULL; | |
176 | ||
ba3a0bc5 | 177 | if (!zprivs) |
178 | { | |
58a9d81c | 179 | fprintf (stderr, "zprivs_init: called with NULL arg!\n"); |
ba3a0bc5 | 180 | exit (1); |
181 | } | |
182 | ||
01245821 | 183 | /* NULL privs */ |
184 | if (! (zprivs->user || zprivs->group | |
185 | || zprivs->cap_num_p || zprivs->cap_num_i) ) | |
186 | { | |
187 | zprivs->change = zprivs_change_null; | |
188 | zprivs->current_state = zprivs_state_null; | |
189 | return; | |
190 | } | |
191 | ||
192 | if (zprivs->user) | |
193 | { | |
194 | if ( (pwentry = getpwnam (zprivs->user)) ) | |
ba3a0bc5 | 195 | { |
196 | zprivs_state.zuid = pwentry->pw_uid; | |
197 | } | |
198 | else | |
199 | { | |
58a9d81c | 200 | /* cant use log.h here as it depends on vty */ |
201 | fprintf (stderr, "privs_init: could not lookup user %s\n", | |
202 | zprivs->user); | |
ba3a0bc5 | 203 | exit (1); |
204 | } | |
205 | } | |
206 | ||
207 | grentry = NULL; | |
208 | ||
209 | if (zprivs->vty_group) | |
210 | /* Add the vty_group to the supplementary groups so it can be chowned to */ | |
211 | { | |
212 | if ( (grentry = getgrnam (zprivs->vty_group)) ) | |
213 | { | |
214 | zprivs_state.vtygrp = grentry->gr_gid; | |
215 | if ( setgroups (1, &zprivs_state.vtygrp) ) | |
216 | { | |
58a9d81c | 217 | fprintf (stderr, "privs_init: could not setgroups, %s\n", |
ba3a0bc5 | 218 | strerror (errno) ); |
219 | exit (1); | |
220 | } | |
221 | } | |
01245821 | 222 | else |
223 | { | |
58a9d81c | 224 | fprintf (stderr, "privs_init: could not lookup vty group %s\n", |
225 | zprivs->vty_group); | |
01245821 | 226 | exit (1); |
227 | } | |
228 | } | |
229 | ||
230 | if (zprivs->group) | |
231 | { | |
ba3a0bc5 | 232 | if ( (grentry = getgrnam (zprivs->group)) ) |
233 | { | |
234 | zprivs_state.zgid = grentry->gr_gid; | |
235 | } | |
01245821 | 236 | else |
237 | { | |
58a9d81c | 238 | fprintf (stderr, "privs_init: could not lookup group %s\n", |
239 | zprivs->group); | |
01245821 | 240 | exit (1); |
241 | } | |
01245821 | 242 | /* change group now, forever. uid we do later */ |
243 | if ( setregid (zprivs_state.zgid, zprivs_state.zgid) ) | |
244 | { | |
58a9d81c | 245 | fprintf (stderr, "zprivs_init: could not setregid, %s\n", |
ba3a0bc5 | 246 | strerror (errno) ); |
01245821 | 247 | exit (1); |
248 | } | |
249 | } | |
250 | ||
251 | #ifdef HAVE_LCAPS | |
252 | zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); | |
253 | zprivs_state.sys_num_p = zprivs->cap_num_p; | |
254 | zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i); | |
255 | zprivs_state.sys_num_i = zprivs->cap_num_i; | |
256 | ||
257 | /* Tell kernel we want caps maintained across uid changes */ | |
258 | if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 ) | |
259 | { | |
58a9d81c | 260 | fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n", |
01245821 | 261 | strerror (errno) ); |
262 | exit(1); | |
263 | } | |
264 | ||
265 | if ( !zprivs_state.syscaps_p ) | |
266 | { | |
58a9d81c | 267 | fprintf (stderr, "privs_init: capabilities enabled, but no capabilities supplied\n"); |
01245821 | 268 | } |
269 | ||
270 | if ( !(zprivs_state.caps = cap_init()) ) | |
271 | { | |
58a9d81c | 272 | fprintf (stderr, "privs_init: failed to cap_init, %s\n", |
273 | strerror (errno)); | |
01245821 | 274 | exit (1); |
275 | } | |
ba3a0bc5 | 276 | |
277 | /* we have caps, we have no need to ever change back the original user */ | |
278 | if (zprivs_state.zuid) | |
279 | { | |
280 | if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) | |
281 | { | |
58a9d81c | 282 | fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n", |
283 | strerror (errno)); | |
ba3a0bc5 | 284 | exit (1); |
285 | } | |
286 | } | |
01245821 | 287 | |
288 | if ( cap_clear (zprivs_state.caps) ) | |
289 | { | |
58a9d81c | 290 | fprintf (stderr, "privs_init: failed to cap_clear, %s\n", |
291 | strerror (errno)); | |
01245821 | 292 | exit (1); |
293 | } | |
294 | ||
295 | /* set permitted caps */ | |
296 | cap_set_flag(zprivs_state.caps, CAP_PERMITTED, | |
297 | zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET); | |
298 | cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE, | |
299 | zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET); | |
300 | ||
01245821 | 301 | /* set inheritable caps, if any */ |
302 | if (zprivs_state.sys_num_i) | |
303 | { | |
304 | cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, | |
305 | zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET); | |
306 | } | |
307 | ||
ba3a0bc5 | 308 | /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as |
309 | * and when, and only when, they are needed. | |
01245821 | 310 | */ |
311 | if ( cap_set_proc (zprivs_state.caps) ) | |
312 | { | |
58a9d81c | 313 | fprintf (stderr, "privs_init: initial cap_set_proc failed\n"); |
01245821 | 314 | exit (1); |
315 | } | |
316 | ||
ba3a0bc5 | 317 | /* set methods for the caller to use */ |
01245821 | 318 | zprivs->change = zprivs_change_caps; |
319 | zprivs->current_state = zprivs_state_caps; | |
320 | ||
321 | #elif !defined(HAVE_LCAPS) | |
322 | /* we dont have caps. we'll need to maintain rid and saved uid | |
323 | * and change euid back to saved uid (who we presume has all neccessary | |
324 | * privileges) whenever we are asked to raise our privileges. | |
325 | */ | |
326 | zprivs_state.zsuid = geteuid(); | |
327 | if ( zprivs_state.zuid ) | |
328 | { | |
329 | if ( setreuid (-1, zprivs_state.zuid) ) | |
330 | { | |
58a9d81c | 331 | fprintf (stderr, "privs_init (uid): could not setreuid, %s\n", |
332 | strerror (errno)); | |
01245821 | 333 | exit (1); |
334 | } | |
335 | } | |
336 | ||
337 | zprivs->change = zprivs_change_uid; | |
338 | zprivs->current_state = zprivs_state_uid; | |
339 | #endif /* HAVE_LCAPS */ | |
340 | } | |
341 | ||
342 | void | |
343 | zprivs_terminate (void) | |
344 | { | |
33b72948 | 345 | |
01245821 | 346 | #ifdef HAVE_LCAPS |
33b72948 | 347 | |
348 | if (zprivs_state.caps) | |
349 | cap_clear (zprivs_state.caps); | |
01245821 | 350 | |
351 | if ( cap_set_proc (zprivs_state.caps) ) | |
352 | { | |
33b72948 | 353 | zlog_err ("privs_terminate: cap_set_proc failed, %s", |
01245821 | 354 | strerror (errno) ); |
355 | exit (1); | |
356 | } | |
357 | ||
33b72948 | 358 | if (zprivs_state.sys_num_p) |
01245821 | 359 | XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p); |
360 | ||
33b72948 | 361 | if (zprivs_state.sys_num_i) |
01245821 | 362 | XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i); |
363 | ||
364 | cap_free (zprivs_state.caps); | |
365 | #else | |
366 | if (zprivs_state.zuid) | |
367 | { | |
368 | if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) ) | |
369 | { | |
33b72948 | 370 | zlog_err ("privs_terminate: could not setreuid, %s", |
01245821 | 371 | strerror (errno) ); |
372 | exit (1); | |
373 | } | |
374 | } | |
375 | #endif /* HAVE_LCAPS */ | |
376 | return; | |
377 | } | |
ba3a0bc5 | 378 | |
379 | void | |
380 | zprivs_get_ids(struct zprivs_ids_t *ids) | |
381 | { | |
382 | ||
383 | ids->uid_priv = getuid(); | |
384 | (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) | |
385 | : (ids->uid_normal = -1); | |
386 | (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) | |
387 | : (ids->gid_normal = -1); | |
388 | (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) | |
389 | : (ids->gid_vty = -1); | |
390 | ||
391 | return; | |
392 | } |