]>
Commit | Line | Data |
---|---|---|
d62a17ae | 1 | /* |
01245821 | 2 | * Zebra privileges. |
3 | * | |
4 | * Copyright (C) 2003 Paul Jakma. | |
01070b9e | 5 | * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. |
01245821 | 6 | * |
7 | * This file is part of GNU Zebra. | |
8 | * | |
9 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2, or (at your option) any | |
12 | * later version. | |
13 | * | |
14 | * GNU Zebra is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
896014f4 DL |
19 | * You should have received a copy of the GNU General Public License along |
20 | * with this program; see the file COPYING; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
01245821 | 22 | */ |
01245821 | 23 | #include <zebra.h> |
24 | #include "log.h" | |
25 | #include "privs.h" | |
26 | #include "memory.h" | |
01245821 | 27 | |
ceacedba | 28 | #ifdef HAVE_CAPABILITIES |
4a1ab8e4 DL |
29 | |
30 | DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information") | |
31 | ||
ceacedba | 32 | /* sort out some generic internal types for: |
33 | * | |
34 | * privilege values (cap_value_t, priv_t) -> pvalue_t | |
35 | * privilege set (..., priv_set_t) -> pset_t | |
36 | * privilege working storage (cap_t, ...) -> pstorage_t | |
37 | * | |
38 | * values we think of as numeric (they're ints really, but we dont know) | |
39 | * sets are mostly opaque, to hold a set of privileges, related in some way. | |
40 | * storage binds together a set of sets we're interested in. | |
41 | * (in reality: cap_value_t and priv_t are ints) | |
d62a17ae | 42 | */ |
ceacedba | 43 | #ifdef HAVE_LCAPS |
44 | /* Linux doesn't have a 'set' type: a set of related privileges */ | |
45 | struct _pset { | |
d62a17ae | 46 | int num; |
47 | cap_value_t *caps; | |
ceacedba | 48 | }; |
49 | typedef cap_value_t pvalue_t; | |
50 | typedef struct _pset pset_t; | |
51 | typedef cap_t pstorage_t; | |
6b0655a2 | 52 | |
d62a17ae | 53 | #elif defined(HAVE_SOLARIS_CAPABILITIES) |
ceacedba | 54 | typedef priv_t pvalue_t; |
55 | typedef priv_set_t pset_t; | |
56 | typedef priv_set_t *pstorage_t; | |
57 | #else /* neither LCAPS nor SOLARIS_CAPABILITIES */ | |
58 | #error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!" | |
59 | #endif /* HAVE_LCAPS */ | |
60 | #endif /* HAVE_CAPABILITIES */ | |
6b0655a2 | 61 | |
ceacedba | 62 | /* the default NULL state we report is RAISED, but could be LOWERED if |
63 | * zprivs_terminate is called and the NULL handler is installed. | |
64 | */ | |
65 | static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED; | |
66 | ||
01245821 | 67 | /* internal privileges state */ |
d62a17ae | 68 | static struct _zprivs_t { |
ceacedba | 69 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 70 | pstorage_t caps; /* working storage */ |
71 | pset_t *syscaps_p; /* system-type requested permitted caps */ | |
72 | pset_t *syscaps_i; /* system-type requested inheritable caps */ | |
73 | #endif /* HAVE_CAPABILITIES */ | |
74 | uid_t zuid, /* uid to run as */ | |
75 | zsuid; /* saved uid */ | |
76 | gid_t zgid; /* gid to run as */ | |
77 | gid_t vtygrp; /* gid for vty sockets */ | |
01245821 | 78 | } zprivs_state; |
79 | ||
80 | /* externally exported but not directly accessed functions */ | |
ceacedba | 81 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 82 | int zprivs_change_caps(zebra_privs_ops_t); |
83 | zebra_privs_current_t zprivs_state_caps(void); | |
ceacedba | 84 | #endif /* HAVE_CAPABILITIES */ |
d62a17ae | 85 | int zprivs_change_uid(zebra_privs_ops_t); |
86 | zebra_privs_current_t zprivs_state_uid(void); | |
87 | int zprivs_change_null(zebra_privs_ops_t); | |
88 | zebra_privs_current_t zprivs_state_null(void); | |
01245821 | 89 | |
ceacedba | 90 | #ifdef HAVE_CAPABILITIES |
91 | /* internal capability API */ | |
d62a17ae | 92 | static pset_t *zcaps2sys(zebra_capabilities_t *, int); |
93 | static void zprivs_caps_init(struct zebra_privs_t *); | |
94 | static void zprivs_caps_terminate(void); | |
ceacedba | 95 | |
96 | /* Map of Quagga abstract capabilities to system capabilities */ | |
d62a17ae | 97 | static struct { |
98 | int num; | |
99 | pvalue_t *system_caps; | |
100 | } cap_map[ZCAP_MAX] = { | |
ceacedba | 101 | #ifdef HAVE_LCAPS /* Quagga -> Linux capabilities mappings */ |
9d303b37 DL |
102 | [ZCAP_SETID] = |
103 | { | |
104 | 2, (pvalue_t[]){CAP_SETGID, CAP_SETUID}, | |
105 | }, | |
106 | [ZCAP_BIND] = | |
107 | { | |
108 | 1, (pvalue_t[]){CAP_NET_BIND_SERVICE}, | |
109 | }, | |
110 | [ZCAP_NET_ADMIN] = | |
111 | { | |
112 | 1, (pvalue_t[]){CAP_NET_ADMIN}, | |
113 | }, | |
114 | [ZCAP_NET_RAW] = | |
115 | { | |
116 | 1, (pvalue_t[]){CAP_NET_RAW}, | |
117 | }, | |
118 | [ZCAP_CHROOT] = | |
119 | { | |
120 | 1, | |
121 | (pvalue_t[]){ | |
122 | CAP_SYS_CHROOT, | |
123 | }, | |
124 | }, | |
125 | [ZCAP_NICE] = | |
126 | { | |
127 | 1, (pvalue_t[]){CAP_SYS_NICE}, | |
128 | }, | |
129 | [ZCAP_PTRACE] = | |
130 | { | |
131 | 1, (pvalue_t[]){CAP_SYS_PTRACE}, | |
132 | }, | |
133 | [ZCAP_DAC_OVERRIDE] = | |
134 | { | |
135 | 1, (pvalue_t[]){CAP_DAC_OVERRIDE}, | |
136 | }, | |
137 | [ZCAP_READ_SEARCH] = | |
138 | { | |
139 | 1, (pvalue_t[]){CAP_DAC_READ_SEARCH}, | |
140 | }, | |
141 | [ZCAP_SYS_ADMIN] = | |
142 | { | |
143 | 1, (pvalue_t[]){CAP_SYS_ADMIN}, | |
144 | }, | |
145 | [ZCAP_FOWNER] = | |
146 | { | |
147 | 1, (pvalue_t[]){CAP_FOWNER}, | |
d62a17ae | 148 | }, |
ceacedba | 149 | #elif defined(HAVE_SOLARIS_CAPABILITIES) /* HAVE_LCAPS */ |
9d303b37 DL |
150 | /* Quagga -> Solaris privilege mappings */ |
151 | [ZCAP_SETID] = | |
152 | { | |
153 | 1, (pvalue_t[]){PRIV_PROC_SETID}, | |
154 | }, | |
155 | [ZCAP_BIND] = | |
156 | { | |
157 | 1, (pvalue_t[]){PRIV_NET_PRIVADDR}, | |
158 | }, | |
d62a17ae | 159 | /* IP_CONFIG is a subset of NET_CONFIG and is allowed in zones */ |
6b148faa | 160 | #ifdef PRIV_SYS_IP_CONFIG |
9d303b37 DL |
161 | [ZCAP_NET_ADMIN] = |
162 | { | |
163 | 1, (pvalue_t[]){PRIV_SYS_IP_CONFIG}, | |
164 | }, | |
6b148faa | 165 | #else |
9d303b37 DL |
166 | [ZCAP_NET_ADMIN] = |
167 | { | |
168 | 1, (pvalue_t[]){PRIV_SYS_NET_CONFIG}, | |
169 | }, | |
6b148faa | 170 | #endif |
9d303b37 DL |
171 | [ZCAP_NET_RAW] = |
172 | { | |
173 | 2, (pvalue_t[]){PRIV_NET_RAWACCESS, | |
174 | PRIV_NET_ICMPACCESS}, | |
175 | }, | |
176 | [ZCAP_CHROOT] = | |
177 | { | |
178 | 1, (pvalue_t[]){PRIV_PROC_CHROOT}, | |
179 | }, | |
180 | [ZCAP_NICE] = | |
181 | { | |
182 | 1, (pvalue_t[]){PRIV_PROC_PRIOCNTL}, | |
183 | }, | |
184 | [ZCAP_PTRACE] = | |
185 | { | |
186 | 1, (pvalue_t[]){PRIV_PROC_SESSION}, | |
187 | }, | |
188 | [ZCAP_DAC_OVERRIDE] = | |
189 | { | |
190 | 5, (pvalue_t[]){PRIV_FILE_DAC_EXECUTE, | |
191 | PRIV_FILE_DAC_READ, | |
192 | PRIV_FILE_DAC_SEARCH, | |
193 | PRIV_FILE_DAC_WRITE, | |
194 | PRIV_FILE_DAC_SEARCH}, | |
195 | }, | |
196 | [ZCAP_READ_SEARCH] = | |
197 | { | |
198 | 2, (pvalue_t[]){PRIV_FILE_DAC_SEARCH, | |
199 | PRIV_FILE_DAC_READ}, | |
200 | }, | |
201 | [ZCAP_SYS_ADMIN] = | |
202 | { | |
203 | 1, (pvalue_t[]){PRIV_SYS_ADMIN}, | |
204 | }, | |
205 | [ZCAP_FOWNER] = | |
206 | { | |
207 | 1, (pvalue_t[]){PRIV_FILE_OWNER}, | |
208 | }, | |
ceacedba | 209 | #endif /* HAVE_SOLARIS_CAPABILITIES */ |
01245821 | 210 | }; |
6b0655a2 | 211 | |
ceacedba | 212 | #ifdef HAVE_LCAPS |
213 | /* Linux forms of capabilities methods */ | |
01245821 | 214 | /* convert zebras privileges to system capabilities */ |
d62a17ae | 215 | static pset_t *zcaps2sys(zebra_capabilities_t *zcaps, int num) |
01245821 | 216 | { |
d62a17ae | 217 | pset_t *syscaps; |
218 | int i, j = 0, count = 0; | |
219 | ||
220 | if (!num) | |
221 | return NULL; | |
222 | ||
223 | /* first count up how many system caps we have */ | |
224 | for (i = 0; i < num; i++) | |
225 | count += cap_map[zcaps[i]].num; | |
226 | ||
227 | if ((syscaps = XCALLOC(MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL) { | |
228 | fprintf(stderr, "%s: could not allocate syscaps!", __func__); | |
229 | return NULL; | |
230 | } | |
231 | ||
232 | syscaps->caps = XCALLOC(MTYPE_PRIVS, (sizeof(pvalue_t) * count)); | |
233 | ||
234 | if (!syscaps->caps) { | |
235 | fprintf(stderr, "%s: could not XCALLOC caps!", __func__); | |
236 | return NULL; | |
237 | } | |
238 | ||
239 | /* copy the capabilities over */ | |
240 | count = 0; | |
241 | for (i = 0; i < num; i++) | |
242 | for (j = 0; j < cap_map[zcaps[i]].num; j++) | |
243 | syscaps->caps[count++] = | |
244 | cap_map[zcaps[i]].system_caps[j]; | |
245 | ||
246 | /* iterations above should be exact same as previous count, obviously.. | |
247 | */ | |
248 | syscaps->num = count; | |
249 | ||
250 | return syscaps; | |
01245821 | 251 | } |
252 | ||
253 | /* set or clear the effective capabilities to/from permitted */ | |
d62a17ae | 254 | int zprivs_change_caps(zebra_privs_ops_t op) |
01245821 | 255 | { |
d62a17ae | 256 | cap_flag_value_t cflag; |
257 | ||
258 | /* should be no possibility of being called without valid caps */ | |
259 | assert(zprivs_state.syscaps_p && zprivs_state.caps); | |
260 | if (!(zprivs_state.syscaps_p && zprivs_state.caps)) | |
261 | exit(1); | |
262 | ||
263 | if (op == ZPRIVS_RAISE) | |
264 | cflag = CAP_SET; | |
265 | else if (op == ZPRIVS_LOWER) | |
266 | cflag = CAP_CLEAR; | |
267 | else | |
268 | return -1; | |
269 | ||
270 | if (!cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE, | |
271 | zprivs_state.syscaps_p->num, | |
272 | zprivs_state.syscaps_p->caps, cflag)) | |
273 | return cap_set_proc(zprivs_state.caps); | |
274 | return -1; | |
01245821 | 275 | } |
276 | ||
d62a17ae | 277 | zebra_privs_current_t zprivs_state_caps(void) |
01245821 | 278 | { |
d62a17ae | 279 | int i; |
280 | cap_flag_value_t val; | |
281 | ||
282 | /* should be no possibility of being called without valid caps */ | |
283 | assert(zprivs_state.syscaps_p && zprivs_state.caps); | |
284 | if (!(zprivs_state.syscaps_p && zprivs_state.caps)) | |
285 | exit(1); | |
286 | ||
287 | for (i = 0; i < zprivs_state.syscaps_p->num; i++) { | |
288 | if (cap_get_flag(zprivs_state.caps, | |
289 | zprivs_state.syscaps_p->caps[i], CAP_EFFECTIVE, | |
290 | &val)) { | |
291 | zlog_warn( | |
292 | "zprivs_state_caps: could not cap_get_flag, %s", | |
293 | safe_strerror(errno)); | |
294 | return ZPRIVS_UNKNOWN; | |
295 | } | |
296 | if (val == CAP_SET) | |
297 | return ZPRIVS_RAISED; | |
298 | } | |
299 | return ZPRIVS_LOWERED; | |
01245821 | 300 | } |
301 | ||
d62a17ae | 302 | static void zprivs_caps_init(struct zebra_privs_t *zprivs) |
ceacedba | 303 | { |
d62a17ae | 304 | zprivs_state.syscaps_p = zcaps2sys(zprivs->caps_p, zprivs->cap_num_p); |
305 | zprivs_state.syscaps_i = zcaps2sys(zprivs->caps_i, zprivs->cap_num_i); | |
306 | ||
307 | /* Tell kernel we want caps maintained across uid changes */ | |
308 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { | |
309 | fprintf(stderr, | |
310 | "privs_init: could not set PR_SET_KEEPCAPS, %s\n", | |
311 | safe_strerror(errno)); | |
312 | exit(1); | |
313 | } | |
314 | ||
315 | /* we have caps, we have no need to ever change back the original user | |
316 | */ | |
317 | /* only change uid if we don't have the correct one */ | |
318 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
319 | if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { | |
320 | fprintf(stderr, | |
321 | "zprivs_init (cap): could not setreuid, %s\n", | |
322 | safe_strerror(errno)); | |
323 | exit(1); | |
324 | } | |
325 | } | |
326 | ||
327 | if (!zprivs_state.syscaps_p) | |
328 | return; | |
329 | ||
330 | if (!(zprivs_state.caps = cap_init())) { | |
331 | fprintf(stderr, "privs_init: failed to cap_init, %s\n", | |
332 | safe_strerror(errno)); | |
333 | exit(1); | |
334 | } | |
335 | ||
336 | if (cap_clear(zprivs_state.caps)) { | |
337 | fprintf(stderr, "privs_init: failed to cap_clear, %s\n", | |
338 | safe_strerror(errno)); | |
339 | exit(1); | |
340 | } | |
341 | ||
342 | /* set permitted caps */ | |
343 | cap_set_flag(zprivs_state.caps, CAP_PERMITTED, | |
344 | zprivs_state.syscaps_p->num, zprivs_state.syscaps_p->caps, | |
345 | CAP_SET); | |
346 | ||
347 | /* set inheritable caps, if any */ | |
348 | if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) { | |
349 | cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, | |
350 | zprivs_state.syscaps_i->num, | |
351 | zprivs_state.syscaps_i->caps, CAP_SET); | |
352 | } | |
353 | ||
354 | /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as | |
355 | * and when, and only when, they are needed. | |
356 | */ | |
357 | if (cap_set_proc(zprivs_state.caps)) { | |
358 | cap_t current_caps; | |
359 | char *current_caps_text = NULL; | |
360 | char *wanted_caps_text = NULL; | |
361 | ||
362 | fprintf(stderr, "privs_init: initial cap_set_proc failed: %s\n", | |
363 | safe_strerror(errno)); | |
364 | ||
365 | current_caps = cap_get_proc(); | |
366 | if (current_caps) { | |
367 | current_caps_text = cap_to_text(current_caps, NULL); | |
368 | cap_free(current_caps); | |
369 | } | |
370 | ||
371 | wanted_caps_text = cap_to_text(zprivs_state.caps, NULL); | |
372 | fprintf(stderr, "Wanted caps: %s\n", | |
373 | wanted_caps_text ? wanted_caps_text : "???"); | |
374 | fprintf(stderr, "Have caps: %s\n", | |
375 | current_caps_text ? current_caps_text : "???"); | |
376 | if (current_caps_text) | |
377 | cap_free(current_caps_text); | |
378 | if (wanted_caps_text) | |
379 | cap_free(wanted_caps_text); | |
380 | ||
381 | exit(1); | |
382 | } | |
383 | ||
384 | /* set methods for the caller to use */ | |
385 | zprivs->change = zprivs_change_caps; | |
386 | zprivs->current_state = zprivs_state_caps; | |
ceacedba | 387 | } |
388 | ||
d62a17ae | 389 | static void zprivs_caps_terminate(void) |
ceacedba | 390 | { |
d62a17ae | 391 | /* clear all capabilities */ |
392 | if (zprivs_state.caps) | |
393 | cap_clear(zprivs_state.caps); | |
394 | ||
395 | /* and boom, capabilities are gone forever */ | |
396 | if (cap_set_proc(zprivs_state.caps)) { | |
397 | fprintf(stderr, "privs_terminate: cap_set_proc failed, %s", | |
398 | safe_strerror(errno)); | |
399 | exit(1); | |
400 | } | |
401 | ||
402 | /* free up private state */ | |
403 | if (zprivs_state.syscaps_p->num) { | |
404 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p->caps); | |
405 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p); | |
406 | } | |
407 | ||
408 | if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) { | |
409 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_i->caps); | |
410 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_i); | |
411 | } | |
412 | ||
413 | cap_free(zprivs_state.caps); | |
ceacedba | 414 | } |
d62a17ae | 415 | #elif defined(HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ |
6b0655a2 | 416 | |
d62a17ae | 417 | /* Solaris specific capability/privilege methods |
ceacedba | 418 | * |
419 | * Resources: | |
420 | * - the 'privileges' man page | |
421 | * - http://cvs.opensolaris.org | |
d62a17ae | 422 | * - |
423 | * http://blogs.sun.com/roller/page/gbrunett?entry=privilege_enabling_set_id_programs1 | |
ceacedba | 424 | */ |
425 | ||
d62a17ae | 426 | static pset_t *zprivs_caps_minimal() |
01070b9e | 427 | { |
d62a17ae | 428 | pset_t *minimal; |
01070b9e | 429 | |
d62a17ae | 430 | if ((minimal = priv_str_to_set("basic", ",", NULL)) == NULL) { |
431 | fprintf(stderr, "%s: couldn't get basic set!\n", __func__); | |
432 | exit(1); | |
433 | } | |
01070b9e | 434 | |
d62a17ae | 435 | /* create a minimal privilege set from the basic set */ |
436 | (void)priv_delset(minimal, PRIV_PROC_EXEC); | |
437 | (void)priv_delset(minimal, PRIV_PROC_INFO); | |
438 | (void)priv_delset(minimal, PRIV_PROC_SESSION); | |
439 | (void)priv_delset(minimal, PRIV_FILE_LINK_ANY); | |
01070b9e | 440 | |
d62a17ae | 441 | return minimal; |
01070b9e BB |
442 | } |
443 | ||
ceacedba | 444 | /* convert zebras privileges to system capabilities */ |
d62a17ae | 445 | static pset_t *zcaps2sys(zebra_capabilities_t *zcaps, int num) |
ceacedba | 446 | { |
d62a17ae | 447 | pset_t *syscaps; |
448 | int i, j = 0; | |
449 | ||
450 | if ((syscaps = priv_allocset()) == NULL) { | |
451 | fprintf(stderr, "%s: could not allocate syscaps!\n", __func__); | |
452 | exit(1); | |
453 | } | |
454 | ||
455 | priv_emptyset(syscaps); | |
456 | ||
457 | for (i = 0; i < num; i++) | |
458 | for (j = 0; j < cap_map[zcaps[i]].num; j++) | |
459 | priv_addset(syscaps, cap_map[zcaps[i]].system_caps[j]); | |
460 | ||
461 | return syscaps; | |
ceacedba | 462 | } |
463 | ||
464 | /* callback exported to users to RAISE and LOWER effective privileges | |
465 | * from nothing to the given permitted set and back down | |
466 | */ | |
d62a17ae | 467 | int zprivs_change_caps(zebra_privs_ops_t op) |
ceacedba | 468 | { |
d62a17ae | 469 | pset_t *privset; |
470 | ||
471 | /* should be no possibility of being called without valid caps */ | |
472 | assert(zprivs_state.syscaps_p); | |
473 | if (!zprivs_state.syscaps_p) { | |
474 | fprintf(stderr, "%s: Eek, missing privileged caps!", __func__); | |
475 | exit(1); | |
476 | } | |
477 | ||
478 | assert(zprivs_state.caps); | |
479 | if (!zprivs_state.caps) { | |
480 | fprintf(stderr, "%s: Eek, missing caps!", __func__); | |
481 | exit(1); | |
482 | } | |
483 | ||
484 | /* to raise: copy original permitted as our working effective set | |
485 | * to lower: copy regular effective set stored in zprivs_state.caps | |
486 | */ | |
487 | if (op == ZPRIVS_RAISE) | |
488 | privset = zprivs_state.syscaps_p; | |
489 | else if (op == ZPRIVS_LOWER) | |
490 | privset = zprivs_state.caps; | |
491 | else | |
492 | return -1; | |
493 | ||
494 | if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privset) != 0) | |
495 | return -1; | |
496 | ||
497 | return 0; | |
ceacedba | 498 | } |
499 | ||
500 | /* Retrieve current privilege state, is it RAISED or LOWERED? */ | |
d62a17ae | 501 | zebra_privs_current_t zprivs_state_caps(void) |
ceacedba | 502 | { |
d62a17ae | 503 | zebra_privs_current_t result; |
504 | pset_t *effective; | |
505 | ||
506 | if ((effective = priv_allocset()) == NULL) { | |
507 | fprintf(stderr, "%s: failed to get priv_allocset! %s\n", | |
508 | __func__, safe_strerror(errno)); | |
509 | return ZPRIVS_UNKNOWN; | |
510 | } | |
511 | ||
512 | if (getppriv(PRIV_EFFECTIVE, effective)) { | |
513 | fprintf(stderr, "%s: failed to get state! %s\n", __func__, | |
514 | safe_strerror(errno)); | |
515 | result = ZPRIVS_UNKNOWN; | |
516 | } else { | |
517 | if (priv_isequalset(effective, zprivs_state.syscaps_p)) | |
518 | result = ZPRIVS_RAISED; | |
519 | else if (priv_isequalset(effective, zprivs_state.caps)) | |
520 | result = ZPRIVS_LOWERED; | |
521 | else | |
522 | result = ZPRIVS_UNKNOWN; | |
523 | } | |
524 | ||
525 | priv_freeset(effective); | |
526 | return result; | |
ceacedba | 527 | } |
528 | ||
d62a17ae | 529 | static void zprivs_caps_init(struct zebra_privs_t *zprivs) |
ceacedba | 530 | { |
d62a17ae | 531 | pset_t *basic; |
532 | pset_t *minimal; | |
533 | ||
534 | /* the specified sets */ | |
535 | zprivs_state.syscaps_p = zcaps2sys(zprivs->caps_p, zprivs->cap_num_p); | |
536 | zprivs_state.syscaps_i = zcaps2sys(zprivs->caps_i, zprivs->cap_num_i); | |
537 | ||
538 | /* nonsensical to have gotten here but not have capabilities */ | |
539 | if (!zprivs_state.syscaps_p) { | |
540 | fprintf(stderr, | |
541 | "%s: capabilities enabled, " | |
542 | "but no valid capabilities supplied\n", | |
543 | __func__); | |
544 | } | |
545 | ||
546 | /* We retain the basic set in our permitted set, as Linux has no | |
547 | * equivalent. The basic set on Linux hence is implicit, always | |
548 | * there. | |
549 | */ | |
550 | if ((basic = priv_str_to_set("basic", ",", NULL)) == NULL) { | |
551 | fprintf(stderr, "%s: couldn't get basic set!\n", __func__); | |
552 | exit(1); | |
553 | } | |
554 | ||
555 | /* Add the basic set to the permitted set */ | |
556 | priv_union(basic, zprivs_state.syscaps_p); | |
557 | priv_freeset(basic); | |
558 | ||
559 | /* Hey kernel, we know about privileges! | |
560 | * this isn't strictly required, use of setppriv should have same effect | |
561 | */ | |
562 | if (setpflags(PRIV_AWARE, 1)) { | |
563 | fprintf(stderr, "%s: error setting PRIV_AWARE!, %s\n", __func__, | |
564 | safe_strerror(errno)); | |
565 | exit(1); | |
566 | } | |
567 | ||
568 | /* need either valid or empty sets for both p and i.. */ | |
569 | assert(zprivs_state.syscaps_i && zprivs_state.syscaps_p); | |
570 | ||
571 | /* we have caps, we have no need to ever change back the original user | |
572 | * change real, effective and saved to the specified user. | |
573 | */ | |
574 | /* only change uid if we don't have the correct one */ | |
575 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
576 | if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { | |
577 | fprintf(stderr, "%s: could not setreuid, %s\n", | |
578 | __func__, safe_strerror(errno)); | |
579 | exit(1); | |
580 | } | |
581 | } | |
582 | ||
583 | /* set the permitted set */ | |
584 | if (setppriv(PRIV_SET, PRIV_PERMITTED, zprivs_state.syscaps_p)) { | |
585 | fprintf(stderr, "%s: error setting permitted set!, %s\n", | |
586 | __func__, safe_strerror(errno)); | |
587 | exit(1); | |
588 | } | |
589 | ||
590 | /* set the inheritable set */ | |
591 | if (setppriv(PRIV_SET, PRIV_INHERITABLE, zprivs_state.syscaps_i)) { | |
592 | fprintf(stderr, "%s: error setting inheritable set!, %s\n", | |
593 | __func__, safe_strerror(errno)); | |
594 | exit(1); | |
595 | } | |
596 | ||
597 | /* we need a minimal basic set for 'effective', potentially for | |
598 | * inheritable too */ | |
599 | minimal = zprivs_caps_minimal(); | |
600 | ||
601 | /* now set the effective set with a subset of basic privileges */ | |
602 | if (setppriv(PRIV_SET, PRIV_EFFECTIVE, minimal)) { | |
603 | fprintf(stderr, "%s: error setting effective set!, %s\n", | |
604 | __func__, safe_strerror(errno)); | |
605 | exit(1); | |
606 | } | |
607 | ||
608 | /* we'll use the minimal set as our working-storage privset */ | |
609 | zprivs_state.caps = minimal; | |
610 | ||
611 | /* set methods for the caller to use */ | |
612 | zprivs->change = zprivs_change_caps; | |
613 | zprivs->current_state = zprivs_state_caps; | |
ceacedba | 614 | } |
615 | ||
d62a17ae | 616 | static void zprivs_caps_terminate(void) |
ceacedba | 617 | { |
d62a17ae | 618 | assert(zprivs_state.caps); |
619 | ||
620 | /* clear all capabilities by using working-storage privset */ | |
621 | setppriv(PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps); | |
622 | setppriv(PRIV_SET, PRIV_PERMITTED, zprivs_state.caps); | |
623 | setppriv(PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps); | |
624 | ||
625 | /* free up private state */ | |
626 | if (zprivs_state.syscaps_p) | |
627 | priv_freeset(zprivs_state.syscaps_p); | |
628 | if (zprivs_state.syscaps_i) | |
629 | priv_freeset(zprivs_state.syscaps_i); | |
630 | ||
631 | priv_freeset(zprivs_state.caps); | |
ceacedba | 632 | } |
633 | #else /* !HAVE_LCAPS && ! HAVE_SOLARIS_CAPABILITIES */ | |
634 | #error "Neither Solaris nor Linux capabilities, dazed and confused..." | |
635 | #endif /* HAVE_LCAPS */ | |
636 | #endif /* HAVE_CAPABILITIES */ | |
6b0655a2 | 637 | |
d62a17ae | 638 | int zprivs_change_uid(zebra_privs_ops_t op) |
01245821 | 639 | { |
d62a17ae | 640 | if (zprivs_state.zsuid == zprivs_state.zuid) |
641 | return 0; | |
642 | if (op == ZPRIVS_RAISE) | |
643 | return seteuid(zprivs_state.zsuid); | |
644 | else if (op == ZPRIVS_LOWER) | |
645 | return seteuid(zprivs_state.zuid); | |
646 | else | |
647 | return -1; | |
01245821 | 648 | } |
649 | ||
d62a17ae | 650 | zebra_privs_current_t zprivs_state_uid(void) |
01245821 | 651 | { |
d62a17ae | 652 | return ((zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED |
653 | : ZPRIVS_RAISED); | |
01245821 | 654 | } |
655 | ||
d62a17ae | 656 | int zprivs_change_null(zebra_privs_ops_t op) |
01245821 | 657 | { |
d62a17ae | 658 | return 0; |
01245821 | 659 | } |
660 | ||
d62a17ae | 661 | zebra_privs_current_t zprivs_state_null(void) |
01245821 | 662 | { |
d62a17ae | 663 | return zprivs_null_state; |
01245821 | 664 | } |
665 | ||
4a9ea50e DL |
666 | #ifndef HAVE_GETGROUPLIST |
667 | /* Solaris 11 has no getgrouplist() */ | |
d62a17ae | 668 | static int getgrouplist(const char *user, gid_t group, gid_t *groups, |
669 | int *ngroups) | |
4a9ea50e | 670 | { |
d62a17ae | 671 | struct group *grp; |
672 | size_t usridx; | |
673 | int pos = 0, ret; | |
674 | ||
675 | if (pos < *ngroups) | |
676 | groups[pos] = group; | |
677 | pos++; | |
678 | ||
679 | setgrent(); | |
680 | while ((grp = getgrent())) { | |
681 | if (grp->gr_gid == group) | |
682 | continue; | |
683 | for (usridx = 0; grp->gr_mem[usridx] != NULL; usridx++) | |
684 | if (!strcmp(grp->gr_mem[usridx], user)) { | |
685 | if (pos < *ngroups) | |
686 | groups[pos] = grp->gr_gid; | |
687 | pos++; | |
688 | break; | |
689 | } | |
690 | } | |
691 | endgrent(); | |
692 | ||
693 | ret = (pos <= *ngroups) ? pos : -1; | |
694 | *ngroups = pos; | |
695 | return ret; | |
4a9ea50e DL |
696 | } |
697 | #endif /* HAVE_GETGROUPLIST */ | |
698 | ||
37a1f2fb | 699 | void zprivs_preinit(struct zebra_privs_t *zprivs) |
01245821 | 700 | { |
d62a17ae | 701 | struct passwd *pwentry = NULL; |
702 | struct group *grentry = NULL; | |
d62a17ae | 703 | |
704 | if (!zprivs) { | |
705 | fprintf(stderr, "zprivs_init: called with NULL arg!\n"); | |
706 | exit(1); | |
707 | } | |
708 | ||
709 | if (zprivs->vty_group) { | |
710 | /* in a "NULL" setup, this is allowed to fail too, but still | |
711 | * try. */ | |
712 | if ((grentry = getgrnam(zprivs->vty_group))) | |
713 | zprivs_state.vtygrp = grentry->gr_gid; | |
714 | else | |
715 | zprivs_state.vtygrp = (gid_t)-1; | |
716 | } | |
717 | ||
718 | /* NULL privs */ | |
719 | if (!(zprivs->user || zprivs->group || zprivs->cap_num_p | |
720 | || zprivs->cap_num_i)) { | |
721 | zprivs->change = zprivs_change_null; | |
722 | zprivs->current_state = zprivs_state_null; | |
723 | return; | |
724 | } | |
725 | ||
726 | if (zprivs->user) { | |
727 | if ((pwentry = getpwnam(zprivs->user)) == NULL) { | |
728 | /* cant use log.h here as it depends on vty */ | |
729 | fprintf(stderr, | |
730 | "privs_init: could not lookup user %s\n", | |
731 | zprivs->user); | |
732 | exit(1); | |
733 | } | |
734 | ||
735 | zprivs_state.zuid = pwentry->pw_uid; | |
736 | zprivs_state.zgid = pwentry->pw_gid; | |
737 | } | |
738 | ||
739 | grentry = NULL; | |
740 | ||
741 | if (zprivs->group) { | |
742 | if ((grentry = getgrnam(zprivs->group)) == NULL) { | |
743 | fprintf(stderr, | |
744 | "privs_init: could not lookup group %s\n", | |
745 | zprivs->group); | |
746 | exit(1); | |
747 | } | |
748 | ||
749 | zprivs_state.zgid = grentry->gr_gid; | |
750 | } | |
37a1f2fb DL |
751 | } |
752 | ||
753 | void zprivs_init(struct zebra_privs_t *zprivs) | |
754 | { | |
755 | gid_t groups[NGROUPS_MAX]; | |
756 | int i, ngroups = 0; | |
757 | int found = 0; | |
758 | ||
759 | /* NULL privs */ | |
760 | if (!(zprivs->user || zprivs->group || zprivs->cap_num_p | |
761 | || zprivs->cap_num_i)) | |
762 | return; | |
d62a17ae | 763 | |
764 | if (zprivs->user) { | |
765 | ngroups = sizeof(groups); | |
766 | if (getgrouplist(zprivs->user, zprivs_state.zgid, groups, | |
767 | &ngroups) | |
768 | < 0) { | |
769 | /* cant use log.h here as it depends on vty */ | |
770 | fprintf(stderr, | |
771 | "privs_init: could not getgrouplist for user %s\n", | |
772 | zprivs->user); | |
773 | exit(1); | |
774 | } | |
775 | } | |
776 | ||
777 | if (zprivs->vty_group) | |
778 | /* Add the vty_group to the supplementary groups so it can be chowned to | |
9d303b37 | 779 | */ |
d62a17ae | 780 | { |
781 | if (zprivs_state.vtygrp == (gid_t)-1) { | |
782 | fprintf(stderr, | |
783 | "privs_init: could not lookup vty group %s\n", | |
784 | zprivs->vty_group); | |
785 | exit(1); | |
786 | } | |
787 | ||
788 | for (i = 0; i < ngroups; i++) | |
789 | if (groups[i] == zprivs_state.vtygrp) { | |
790 | found++; | |
791 | break; | |
792 | } | |
793 | ||
794 | if (!found) { | |
795 | fprintf(stderr, | |
796 | "privs_init: user(%s) is not part of vty group specified(%s)\n", | |
797 | zprivs->user, zprivs->vty_group); | |
798 | exit(1); | |
799 | } | |
800 | if (i >= ngroups && ngroups < (int)ZEBRA_NUM_OF(groups)) { | |
801 | groups[i] = zprivs_state.vtygrp; | |
802 | } | |
803 | } | |
804 | ||
805 | zprivs_state.zsuid = geteuid(); /* initial uid */ | |
806 | /* add groups only if we changed uid - otherwise skip */ | |
807 | if ((ngroups) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
808 | if (setgroups(ngroups, groups)) { | |
809 | fprintf(stderr, "privs_init: could not setgroups, %s\n", | |
810 | safe_strerror(errno)); | |
811 | exit(1); | |
812 | } | |
813 | } | |
814 | ||
815 | /* change gid only if we changed uid - otherwise skip */ | |
816 | if ((zprivs_state.zgid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
817 | /* change group now, forever. uid we do later */ | |
818 | if (setregid(zprivs_state.zgid, zprivs_state.zgid)) { | |
819 | fprintf(stderr, "zprivs_init: could not setregid, %s\n", | |
820 | safe_strerror(errno)); | |
821 | exit(1); | |
822 | } | |
823 | } | |
824 | ||
ceacedba | 825 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 826 | zprivs_caps_init(zprivs); |
8e04538c DS |
827 | |
828 | /* | |
829 | * If we have initialized the system with no requested | |
830 | * capabilities, change will not have been set | |
831 | * to anything by zprivs_caps_init, As such | |
832 | * we should make sure that when we attempt | |
833 | * to raize privileges that we actually have | |
834 | * a do nothing function to call instead of a | |
835 | * crash :). | |
836 | */ | |
837 | if (!zprivs->change) | |
838 | zprivs->change = zprivs_change_null; | |
839 | ||
d62a17ae | 840 | #else /* !HAVE_CAPABILITIES */ |
841 | /* we dont have caps. we'll need to maintain rid and saved uid | |
842 | * and change euid back to saved uid (who we presume has all neccessary | |
843 | * privileges) whenever we are asked to raise our privileges. | |
844 | * | |
845 | * This is not worth that much security wise, but all we can do. | |
846 | */ | |
847 | zprivs_state.zsuid = geteuid(); | |
848 | /* only change uid if we don't have the correct one */ | |
849 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
850 | if (setreuid(-1, zprivs_state.zuid)) { | |
851 | fprintf(stderr, | |
852 | "privs_init (uid): could not setreuid, %s\n", | |
853 | safe_strerror(errno)); | |
854 | exit(1); | |
855 | } | |
856 | } | |
857 | ||
858 | zprivs->change = zprivs_change_uid; | |
859 | zprivs->current_state = zprivs_state_uid; | |
ceacedba | 860 | #endif /* HAVE_CAPABILITIES */ |
01245821 | 861 | } |
862 | ||
d62a17ae | 863 | void zprivs_terminate(struct zebra_privs_t *zprivs) |
01245821 | 864 | { |
d62a17ae | 865 | if (!zprivs) { |
866 | fprintf(stderr, "%s: no privs struct given, terminating", | |
867 | __func__); | |
868 | exit(0); | |
869 | } | |
870 | ||
ceacedba | 871 | #ifdef HAVE_CAPABILITIES |
4093d47b DL |
872 | if (zprivs->user || zprivs->group || zprivs->cap_num_p |
873 | || zprivs->cap_num_i) | |
874 | zprivs_caps_terminate(); | |
d62a17ae | 875 | #else /* !HAVE_CAPABILITIES */ |
876 | /* only change uid if we don't have the correct one */ | |
877 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
878 | if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { | |
879 | fprintf(stderr, | |
880 | "privs_terminate: could not setreuid, %s", | |
881 | safe_strerror(errno)); | |
882 | exit(1); | |
883 | } | |
884 | } | |
01245821 | 885 | #endif /* HAVE_LCAPS */ |
ceacedba | 886 | |
d62a17ae | 887 | zprivs->change = zprivs_change_null; |
888 | zprivs->current_state = zprivs_state_null; | |
889 | zprivs_null_state = ZPRIVS_LOWERED; | |
890 | return; | |
01245821 | 891 | } |
ba3a0bc5 | 892 | |
d62a17ae | 893 | void zprivs_get_ids(struct zprivs_ids_t *ids) |
ba3a0bc5 | 894 | { |
895 | ||
d62a17ae | 896 | ids->uid_priv = getuid(); |
897 | (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) | |
898 | : (ids->uid_normal = -1); | |
899 | (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) | |
900 | : (ids->gid_normal = -1); | |
901 | (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) | |
902 | : (ids->gid_vty = -1); | |
903 | ||
904 | return; | |
ba3a0bc5 | 905 | } |