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