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