]>
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" | |
00dffa8c | 27 | #include "frr_pthread.h" |
ff245f0e | 28 | #include "lib_errors.h" |
8875d051 | 29 | #include "lib/queue.h" |
01245821 | 30 | |
bf8d3d6a | 31 | DEFINE_MTYPE_STATIC(LIB, PRIVS, "Privilege information"); |
8875d051 MS |
32 | |
33 | /* | |
34 | * Different capabilities/privileges apis have different characteristics: some | |
35 | * are process-wide, and some are per-thread. | |
36 | */ | |
ceacedba | 37 | #ifdef HAVE_CAPABILITIES |
8875d051 MS |
38 | #ifdef HAVE_LCAPS |
39 | static const bool privs_per_process; /* = false */ | |
8875d051 MS |
40 | #else |
41 | static const bool privs_per_process = true; | |
cae8bc96 DS |
42 | #endif /* HAVE_LCAPS */ |
43 | #else /* HAVE_CAPABILITIES */ | |
44 | static const bool privs_per_process = true; | |
45 | #endif | |
4a1ab8e4 | 46 | |
8875d051 | 47 | #ifdef HAVE_CAPABILITIES |
4a1ab8e4 | 48 | |
ceacedba | 49 | /* sort out some generic internal types for: |
50 | * | |
51 | * privilege values (cap_value_t, priv_t) -> pvalue_t | |
52 | * privilege set (..., priv_set_t) -> pset_t | |
53 | * privilege working storage (cap_t, ...) -> pstorage_t | |
54 | * | |
55 | * values we think of as numeric (they're ints really, but we dont know) | |
56 | * sets are mostly opaque, to hold a set of privileges, related in some way. | |
57 | * storage binds together a set of sets we're interested in. | |
58 | * (in reality: cap_value_t and priv_t are ints) | |
d62a17ae | 59 | */ |
ceacedba | 60 | #ifdef HAVE_LCAPS |
61 | /* Linux doesn't have a 'set' type: a set of related privileges */ | |
62 | struct _pset { | |
d62a17ae | 63 | int num; |
64 | cap_value_t *caps; | |
ceacedba | 65 | }; |
66 | typedef cap_value_t pvalue_t; | |
67 | typedef struct _pset pset_t; | |
68 | typedef cap_t pstorage_t; | |
6b0655a2 | 69 | |
cae8bc96 | 70 | #else /* no LCAPS */ |
ceacedba | 71 | #error "HAVE_CAPABILITIES defined, but neither LCAPS nor Solaris Capabilties!" |
72 | #endif /* HAVE_LCAPS */ | |
73 | #endif /* HAVE_CAPABILITIES */ | |
6b0655a2 | 74 | |
ceacedba | 75 | /* the default NULL state we report is RAISED, but could be LOWERED if |
76 | * zprivs_terminate is called and the NULL handler is installed. | |
77 | */ | |
78 | static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED; | |
79 | ||
01245821 | 80 | /* internal privileges state */ |
d62a17ae | 81 | static struct _zprivs_t { |
ceacedba | 82 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 83 | pstorage_t caps; /* working storage */ |
84 | pset_t *syscaps_p; /* system-type requested permitted caps */ | |
85 | pset_t *syscaps_i; /* system-type requested inheritable caps */ | |
86 | #endif /* HAVE_CAPABILITIES */ | |
87 | uid_t zuid, /* uid to run as */ | |
88 | zsuid; /* saved uid */ | |
89 | gid_t zgid; /* gid to run as */ | |
90 | gid_t vtygrp; /* gid for vty sockets */ | |
01245821 | 91 | } zprivs_state; |
92 | ||
93 | /* externally exported but not directly accessed functions */ | |
ceacedba | 94 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 95 | int zprivs_change_caps(zebra_privs_ops_t); |
96 | zebra_privs_current_t zprivs_state_caps(void); | |
ceacedba | 97 | #endif /* HAVE_CAPABILITIES */ |
d62a17ae | 98 | int zprivs_change_uid(zebra_privs_ops_t); |
99 | zebra_privs_current_t zprivs_state_uid(void); | |
100 | int zprivs_change_null(zebra_privs_ops_t); | |
101 | zebra_privs_current_t zprivs_state_null(void); | |
01245821 | 102 | |
ceacedba | 103 | #ifdef HAVE_CAPABILITIES |
104 | /* internal capability API */ | |
d62a17ae | 105 | static pset_t *zcaps2sys(zebra_capabilities_t *, int); |
106 | static void zprivs_caps_init(struct zebra_privs_t *); | |
107 | static void zprivs_caps_terminate(void); | |
ceacedba | 108 | |
109 | /* Map of Quagga abstract capabilities to system capabilities */ | |
d62a17ae | 110 | static struct { |
111 | int num; | |
112 | pvalue_t *system_caps; | |
113 | } cap_map[ZCAP_MAX] = { | |
ceacedba | 114 | #ifdef HAVE_LCAPS /* Quagga -> Linux capabilities mappings */ |
9d303b37 DL |
115 | [ZCAP_SETID] = |
116 | { | |
117 | 2, (pvalue_t[]){CAP_SETGID, CAP_SETUID}, | |
118 | }, | |
119 | [ZCAP_BIND] = | |
120 | { | |
121 | 1, (pvalue_t[]){CAP_NET_BIND_SERVICE}, | |
122 | }, | |
123 | [ZCAP_NET_ADMIN] = | |
124 | { | |
125 | 1, (pvalue_t[]){CAP_NET_ADMIN}, | |
126 | }, | |
127 | [ZCAP_NET_RAW] = | |
128 | { | |
129 | 1, (pvalue_t[]){CAP_NET_RAW}, | |
130 | }, | |
131 | [ZCAP_CHROOT] = | |
132 | { | |
133 | 1, | |
134 | (pvalue_t[]){ | |
135 | CAP_SYS_CHROOT, | |
136 | }, | |
137 | }, | |
138 | [ZCAP_NICE] = | |
139 | { | |
140 | 1, (pvalue_t[]){CAP_SYS_NICE}, | |
141 | }, | |
142 | [ZCAP_PTRACE] = | |
143 | { | |
144 | 1, (pvalue_t[]){CAP_SYS_PTRACE}, | |
145 | }, | |
146 | [ZCAP_DAC_OVERRIDE] = | |
147 | { | |
148 | 1, (pvalue_t[]){CAP_DAC_OVERRIDE}, | |
149 | }, | |
150 | [ZCAP_READ_SEARCH] = | |
151 | { | |
152 | 1, (pvalue_t[]){CAP_DAC_READ_SEARCH}, | |
153 | }, | |
154 | [ZCAP_SYS_ADMIN] = | |
155 | { | |
156 | 1, (pvalue_t[]){CAP_SYS_ADMIN}, | |
157 | }, | |
158 | [ZCAP_FOWNER] = | |
159 | { | |
160 | 1, (pvalue_t[]){CAP_FOWNER}, | |
d62a17ae | 161 | }, |
575a2597 DS |
162 | [ZCAP_IPC_LOCK] = |
163 | { | |
164 | 1, (pvalue_t[]){CAP_IPC_LOCK}, | |
165 | }, | |
92b06d6b AK |
166 | [ZCAP_SYS_RAWIO] = |
167 | { | |
168 | 1, (pvalue_t[]){CAP_SYS_RAWIO}, | |
169 | }, | |
cae8bc96 | 170 | #endif /* HAVE_LCAPS */ |
01245821 | 171 | }; |
6b0655a2 | 172 | |
ceacedba | 173 | #ifdef HAVE_LCAPS |
174 | /* Linux forms of capabilities methods */ | |
01245821 | 175 | /* convert zebras privileges to system capabilities */ |
d62a17ae | 176 | static pset_t *zcaps2sys(zebra_capabilities_t *zcaps, int num) |
01245821 | 177 | { |
d62a17ae | 178 | pset_t *syscaps; |
179 | int i, j = 0, count = 0; | |
180 | ||
181 | if (!num) | |
182 | return NULL; | |
183 | ||
184 | /* first count up how many system caps we have */ | |
185 | for (i = 0; i < num; i++) | |
186 | count += cap_map[zcaps[i]].num; | |
187 | ||
188 | if ((syscaps = XCALLOC(MTYPE_PRIVS, (sizeof(pset_t) * num))) == NULL) { | |
189 | fprintf(stderr, "%s: could not allocate syscaps!", __func__); | |
190 | return NULL; | |
191 | } | |
192 | ||
193 | syscaps->caps = XCALLOC(MTYPE_PRIVS, (sizeof(pvalue_t) * count)); | |
194 | ||
195 | if (!syscaps->caps) { | |
196 | fprintf(stderr, "%s: could not XCALLOC caps!", __func__); | |
197 | return NULL; | |
198 | } | |
199 | ||
200 | /* copy the capabilities over */ | |
201 | count = 0; | |
202 | for (i = 0; i < num; i++) | |
203 | for (j = 0; j < cap_map[zcaps[i]].num; j++) | |
204 | syscaps->caps[count++] = | |
205 | cap_map[zcaps[i]].system_caps[j]; | |
206 | ||
207 | /* iterations above should be exact same as previous count, obviously.. | |
208 | */ | |
209 | syscaps->num = count; | |
210 | ||
211 | return syscaps; | |
01245821 | 212 | } |
213 | ||
214 | /* set or clear the effective capabilities to/from permitted */ | |
d62a17ae | 215 | int zprivs_change_caps(zebra_privs_ops_t op) |
01245821 | 216 | { |
d62a17ae | 217 | cap_flag_value_t cflag; |
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); | |
223 | ||
224 | if (op == ZPRIVS_RAISE) | |
225 | cflag = CAP_SET; | |
226 | else if (op == ZPRIVS_LOWER) | |
227 | cflag = CAP_CLEAR; | |
228 | else | |
229 | return -1; | |
230 | ||
231 | if (!cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE, | |
232 | zprivs_state.syscaps_p->num, | |
233 | zprivs_state.syscaps_p->caps, cflag)) | |
234 | return cap_set_proc(zprivs_state.caps); | |
235 | return -1; | |
01245821 | 236 | } |
237 | ||
d62a17ae | 238 | zebra_privs_current_t zprivs_state_caps(void) |
01245821 | 239 | { |
d62a17ae | 240 | int i; |
241 | cap_flag_value_t val; | |
242 | ||
243 | /* should be no possibility of being called without valid caps */ | |
244 | assert(zprivs_state.syscaps_p && zprivs_state.caps); | |
245 | if (!(zprivs_state.syscaps_p && zprivs_state.caps)) | |
246 | exit(1); | |
247 | ||
248 | for (i = 0; i < zprivs_state.syscaps_p->num; i++) { | |
249 | if (cap_get_flag(zprivs_state.caps, | |
250 | zprivs_state.syscaps_p->caps[i], CAP_EFFECTIVE, | |
251 | &val)) { | |
ff245f0e | 252 | flog_err( |
450971aa | 253 | EC_LIB_SYSTEM_CALL, |
d62a17ae | 254 | "zprivs_state_caps: could not cap_get_flag, %s", |
255 | safe_strerror(errno)); | |
256 | return ZPRIVS_UNKNOWN; | |
257 | } | |
258 | if (val == CAP_SET) | |
259 | return ZPRIVS_RAISED; | |
260 | } | |
261 | return ZPRIVS_LOWERED; | |
01245821 | 262 | } |
263 | ||
eb23e17f RZ |
264 | /** Release private cap state if allocated. */ |
265 | static void zprivs_state_free_caps(void) | |
266 | { | |
267 | if (zprivs_state.syscaps_p) { | |
268 | if (zprivs_state.syscaps_p->num) | |
269 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p->caps); | |
270 | ||
271 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_p); | |
272 | } | |
273 | ||
274 | if (zprivs_state.syscaps_i) { | |
275 | if (zprivs_state.syscaps_i->num) | |
276 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_i->caps); | |
277 | ||
278 | XFREE(MTYPE_PRIVS, zprivs_state.syscaps_i); | |
279 | } | |
280 | ||
281 | if (zprivs_state.caps) { | |
282 | cap_free(zprivs_state.caps); | |
283 | zprivs_state.caps = NULL; | |
284 | } | |
285 | } | |
286 | ||
d62a17ae | 287 | static void zprivs_caps_init(struct zebra_privs_t *zprivs) |
ceacedba | 288 | { |
eb23e17f RZ |
289 | /* Release allocated zcaps if this function was called before. */ |
290 | zprivs_state_free_caps(); | |
291 | ||
d62a17ae | 292 | zprivs_state.syscaps_p = zcaps2sys(zprivs->caps_p, zprivs->cap_num_p); |
293 | zprivs_state.syscaps_i = zcaps2sys(zprivs->caps_i, zprivs->cap_num_i); | |
294 | ||
295 | /* Tell kernel we want caps maintained across uid changes */ | |
296 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { | |
297 | fprintf(stderr, | |
298 | "privs_init: could not set PR_SET_KEEPCAPS, %s\n", | |
299 | safe_strerror(errno)); | |
300 | exit(1); | |
301 | } | |
302 | ||
303 | /* we have caps, we have no need to ever change back the original user | |
304 | */ | |
305 | /* only change uid if we don't have the correct one */ | |
306 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
307 | if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { | |
308 | fprintf(stderr, | |
309 | "zprivs_init (cap): could not setreuid, %s\n", | |
310 | safe_strerror(errno)); | |
311 | exit(1); | |
312 | } | |
313 | } | |
314 | ||
d62a17ae | 315 | if (!(zprivs_state.caps = cap_init())) { |
316 | fprintf(stderr, "privs_init: failed to cap_init, %s\n", | |
317 | safe_strerror(errno)); | |
318 | exit(1); | |
319 | } | |
320 | ||
321 | if (cap_clear(zprivs_state.caps)) { | |
322 | fprintf(stderr, "privs_init: failed to cap_clear, %s\n", | |
323 | safe_strerror(errno)); | |
324 | exit(1); | |
325 | } | |
326 | ||
88be4045 DS |
327 | /* set permitted caps, if any */ |
328 | if (zprivs_state.syscaps_p && zprivs_state.syscaps_p->num) { | |
329 | cap_set_flag(zprivs_state.caps, CAP_PERMITTED, | |
330 | zprivs_state.syscaps_p->num, | |
331 | zprivs_state.syscaps_p->caps, CAP_SET); | |
332 | } | |
d62a17ae | 333 | |
334 | /* set inheritable caps, if any */ | |
335 | if (zprivs_state.syscaps_i && zprivs_state.syscaps_i->num) { | |
336 | cap_set_flag(zprivs_state.caps, CAP_INHERITABLE, | |
337 | zprivs_state.syscaps_i->num, | |
338 | zprivs_state.syscaps_i->caps, CAP_SET); | |
339 | } | |
340 | ||
341 | /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as | |
342 | * and when, and only when, they are needed. | |
343 | */ | |
344 | if (cap_set_proc(zprivs_state.caps)) { | |
345 | cap_t current_caps; | |
346 | char *current_caps_text = NULL; | |
347 | char *wanted_caps_text = NULL; | |
348 | ||
349 | fprintf(stderr, "privs_init: initial cap_set_proc failed: %s\n", | |
350 | safe_strerror(errno)); | |
351 | ||
352 | current_caps = cap_get_proc(); | |
353 | if (current_caps) { | |
354 | current_caps_text = cap_to_text(current_caps, NULL); | |
355 | cap_free(current_caps); | |
356 | } | |
357 | ||
358 | wanted_caps_text = cap_to_text(zprivs_state.caps, NULL); | |
359 | fprintf(stderr, "Wanted caps: %s\n", | |
360 | wanted_caps_text ? wanted_caps_text : "???"); | |
361 | fprintf(stderr, "Have caps: %s\n", | |
362 | current_caps_text ? current_caps_text : "???"); | |
363 | if (current_caps_text) | |
364 | cap_free(current_caps_text); | |
365 | if (wanted_caps_text) | |
366 | cap_free(wanted_caps_text); | |
367 | ||
368 | exit(1); | |
369 | } | |
370 | ||
371 | /* set methods for the caller to use */ | |
372 | zprivs->change = zprivs_change_caps; | |
373 | zprivs->current_state = zprivs_state_caps; | |
ceacedba | 374 | } |
375 | ||
d62a17ae | 376 | static void zprivs_caps_terminate(void) |
ceacedba | 377 | { |
d617669d | 378 | /* Clear all capabilities, if we have any. */ |
d62a17ae | 379 | if (zprivs_state.caps) |
380 | cap_clear(zprivs_state.caps); | |
d617669d MS |
381 | else |
382 | return; | |
d62a17ae | 383 | |
384 | /* and boom, capabilities are gone forever */ | |
385 | if (cap_set_proc(zprivs_state.caps)) { | |
386 | fprintf(stderr, "privs_terminate: cap_set_proc failed, %s", | |
387 | safe_strerror(errno)); | |
388 | exit(1); | |
389 | } | |
390 | ||
eb23e17f | 391 | zprivs_state_free_caps(); |
ceacedba | 392 | } |
cae8bc96 DS |
393 | #else /* !HAVE_LCAPS */ |
394 | #error "no Linux capabilities, dazed and confused..." | |
ceacedba | 395 | #endif /* HAVE_LCAPS */ |
396 | #endif /* HAVE_CAPABILITIES */ | |
6b0655a2 | 397 | |
d62a17ae | 398 | int zprivs_change_uid(zebra_privs_ops_t op) |
01245821 | 399 | { |
d62a17ae | 400 | if (zprivs_state.zsuid == zprivs_state.zuid) |
401 | return 0; | |
402 | if (op == ZPRIVS_RAISE) | |
403 | return seteuid(zprivs_state.zsuid); | |
404 | else if (op == ZPRIVS_LOWER) | |
405 | return seteuid(zprivs_state.zuid); | |
406 | else | |
407 | return -1; | |
01245821 | 408 | } |
409 | ||
d62a17ae | 410 | zebra_privs_current_t zprivs_state_uid(void) |
01245821 | 411 | { |
d62a17ae | 412 | return ((zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED |
413 | : ZPRIVS_RAISED); | |
01245821 | 414 | } |
415 | ||
d62a17ae | 416 | int zprivs_change_null(zebra_privs_ops_t op) |
01245821 | 417 | { |
d62a17ae | 418 | return 0; |
01245821 | 419 | } |
420 | ||
d62a17ae | 421 | zebra_privs_current_t zprivs_state_null(void) |
01245821 | 422 | { |
d62a17ae | 423 | return zprivs_null_state; |
01245821 | 424 | } |
425 | ||
4a9ea50e DL |
426 | #ifndef HAVE_GETGROUPLIST |
427 | /* Solaris 11 has no getgrouplist() */ | |
d62a17ae | 428 | static int getgrouplist(const char *user, gid_t group, gid_t *groups, |
429 | int *ngroups) | |
4a9ea50e | 430 | { |
d62a17ae | 431 | struct group *grp; |
432 | size_t usridx; | |
433 | int pos = 0, ret; | |
434 | ||
435 | if (pos < *ngroups) | |
436 | groups[pos] = group; | |
437 | pos++; | |
438 | ||
439 | setgrent(); | |
440 | while ((grp = getgrent())) { | |
441 | if (grp->gr_gid == group) | |
442 | continue; | |
443 | for (usridx = 0; grp->gr_mem[usridx] != NULL; usridx++) | |
444 | if (!strcmp(grp->gr_mem[usridx], user)) { | |
445 | if (pos < *ngroups) | |
446 | groups[pos] = grp->gr_gid; | |
447 | pos++; | |
448 | break; | |
449 | } | |
450 | } | |
451 | endgrent(); | |
452 | ||
453 | ret = (pos <= *ngroups) ? pos : -1; | |
454 | *ngroups = pos; | |
455 | return ret; | |
4a9ea50e DL |
456 | } |
457 | #endif /* HAVE_GETGROUPLIST */ | |
458 | ||
8875d051 MS |
459 | /* |
460 | * Helper function that locates a refcounting object to use: a process-wide | |
461 | * object or a per-pthread object. | |
462 | */ | |
463 | static struct zebra_privs_refs_t *get_privs_refs(struct zebra_privs_t *privs) | |
464 | { | |
465 | struct zebra_privs_refs_t *temp, *refs = NULL; | |
466 | pthread_t tid; | |
467 | ||
468 | if (privs_per_process) | |
469 | refs = &(privs->process_refs); | |
470 | else { | |
471 | /* Locate - or create - the object for the current pthread. */ | |
472 | tid = pthread_self(); | |
473 | ||
474 | STAILQ_FOREACH(temp, &(privs->thread_refs), entry) { | |
475 | if (pthread_equal(temp->tid, tid)) { | |
476 | refs = temp; | |
477 | break; | |
478 | } | |
479 | } | |
480 | ||
481 | /* Need to create a new refcounting object. */ | |
482 | if (refs == NULL) { | |
483 | refs = XCALLOC(MTYPE_PRIVS, | |
484 | sizeof(struct zebra_privs_refs_t)); | |
485 | refs->tid = tid; | |
486 | STAILQ_INSERT_TAIL(&(privs->thread_refs), refs, entry); | |
487 | } | |
488 | } | |
489 | ||
490 | return refs; | |
491 | } | |
492 | ||
6017c3a2 DL |
493 | struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs, |
494 | const char *funcname) | |
495 | { | |
496 | int save_errno = errno; | |
8875d051 | 497 | struct zebra_privs_refs_t *refs; |
6017c3a2 DL |
498 | |
499 | if (!privs) | |
500 | return NULL; | |
501 | ||
8875d051 MS |
502 | /* |
503 | * Serialize 'raise' operations; particularly important for | |
504 | * OSes where privs are process-wide. | |
505 | */ | |
cb1991af | 506 | frr_with_mutex (&(privs->mutex)) { |
8875d051 MS |
507 | /* Locate ref-counting object to use */ |
508 | refs = get_privs_refs(privs); | |
509 | ||
510 | if (++(refs->refcount) == 1) { | |
064e2f32 MS |
511 | errno = 0; |
512 | if (privs->change(ZPRIVS_RAISE)) { | |
513 | zlog_err("%s: Failed to raise privileges (%s)", | |
514 | funcname, safe_strerror(errno)); | |
515 | } | |
516 | errno = save_errno; | |
8875d051 | 517 | refs->raised_in_funcname = funcname; |
064e2f32 | 518 | } |
c5c44d4b | 519 | } |
c5c44d4b | 520 | |
6017c3a2 DL |
521 | return privs; |
522 | } | |
523 | ||
524 | void _zprivs_lower(struct zebra_privs_t **privs) | |
525 | { | |
526 | int save_errno = errno; | |
8875d051 | 527 | struct zebra_privs_refs_t *refs; |
6017c3a2 DL |
528 | |
529 | if (!*privs) | |
530 | return; | |
531 | ||
8875d051 MS |
532 | /* Serialize 'lower privs' operation - particularly important |
533 | * when OS privs are process-wide. | |
534 | */ | |
cb1991af | 535 | frr_with_mutex (&(*privs)->mutex) { |
8875d051 MS |
536 | refs = get_privs_refs(*privs); |
537 | ||
538 | if (--(refs->refcount) == 0) { | |
064e2f32 MS |
539 | errno = 0; |
540 | if ((*privs)->change(ZPRIVS_LOWER)) { | |
541 | zlog_err("%s: Failed to lower privileges (%s)", | |
8875d051 | 542 | refs->raised_in_funcname, |
064e2f32 MS |
543 | safe_strerror(errno)); |
544 | } | |
545 | errno = save_errno; | |
8875d051 | 546 | refs->raised_in_funcname = NULL; |
064e2f32 | 547 | } |
c5c44d4b | 548 | } |
c5c44d4b | 549 | |
6017c3a2 DL |
550 | *privs = NULL; |
551 | } | |
552 | ||
37a1f2fb | 553 | void zprivs_preinit(struct zebra_privs_t *zprivs) |
01245821 | 554 | { |
d62a17ae | 555 | struct passwd *pwentry = NULL; |
556 | struct group *grentry = NULL; | |
d62a17ae | 557 | |
558 | if (!zprivs) { | |
559 | fprintf(stderr, "zprivs_init: called with NULL arg!\n"); | |
560 | exit(1); | |
561 | } | |
562 | ||
c5c44d4b | 563 | pthread_mutex_init(&(zprivs->mutex), NULL); |
8875d051 MS |
564 | zprivs->process_refs.refcount = 0; |
565 | zprivs->process_refs.raised_in_funcname = NULL; | |
566 | STAILQ_INIT(&zprivs->thread_refs); | |
c5c44d4b | 567 | |
d62a17ae | 568 | if (zprivs->vty_group) { |
569 | /* in a "NULL" setup, this is allowed to fail too, but still | |
570 | * try. */ | |
571 | if ((grentry = getgrnam(zprivs->vty_group))) | |
572 | zprivs_state.vtygrp = grentry->gr_gid; | |
573 | else | |
574 | zprivs_state.vtygrp = (gid_t)-1; | |
575 | } | |
576 | ||
577 | /* NULL privs */ | |
578 | if (!(zprivs->user || zprivs->group || zprivs->cap_num_p | |
579 | || zprivs->cap_num_i)) { | |
580 | zprivs->change = zprivs_change_null; | |
581 | zprivs->current_state = zprivs_state_null; | |
582 | return; | |
583 | } | |
584 | ||
585 | if (zprivs->user) { | |
586 | if ((pwentry = getpwnam(zprivs->user)) == NULL) { | |
587 | /* cant use log.h here as it depends on vty */ | |
588 | fprintf(stderr, | |
589 | "privs_init: could not lookup user %s\n", | |
590 | zprivs->user); | |
591 | exit(1); | |
592 | } | |
593 | ||
594 | zprivs_state.zuid = pwentry->pw_uid; | |
595 | zprivs_state.zgid = pwentry->pw_gid; | |
596 | } | |
597 | ||
598 | grentry = NULL; | |
599 | ||
600 | if (zprivs->group) { | |
601 | if ((grentry = getgrnam(zprivs->group)) == NULL) { | |
602 | fprintf(stderr, | |
603 | "privs_init: could not lookup group %s\n", | |
604 | zprivs->group); | |
605 | exit(1); | |
606 | } | |
607 | ||
608 | zprivs_state.zgid = grentry->gr_gid; | |
609 | } | |
37a1f2fb DL |
610 | } |
611 | ||
877057b6 DL |
612 | struct zebra_privs_t *lib_privs; |
613 | ||
37a1f2fb DL |
614 | void zprivs_init(struct zebra_privs_t *zprivs) |
615 | { | |
1c77d034 | 616 | gid_t groups[NGROUPS_MAX] = {}; |
37a1f2fb DL |
617 | int i, ngroups = 0; |
618 | int found = 0; | |
619 | ||
620 | /* NULL privs */ | |
621 | if (!(zprivs->user || zprivs->group || zprivs->cap_num_p | |
622 | || zprivs->cap_num_i)) | |
623 | return; | |
d62a17ae | 624 | |
877057b6 DL |
625 | lib_privs = zprivs; |
626 | ||
d62a17ae | 627 | if (zprivs->user) { |
72de5f4b | 628 | ngroups = array_size(groups); |
d62a17ae | 629 | if (getgrouplist(zprivs->user, zprivs_state.zgid, groups, |
630 | &ngroups) | |
631 | < 0) { | |
632 | /* cant use log.h here as it depends on vty */ | |
633 | fprintf(stderr, | |
634 | "privs_init: could not getgrouplist for user %s\n", | |
635 | zprivs->user); | |
636 | exit(1); | |
637 | } | |
638 | } | |
639 | ||
640 | if (zprivs->vty_group) | |
641 | /* Add the vty_group to the supplementary groups so it can be chowned to | |
9d303b37 | 642 | */ |
d62a17ae | 643 | { |
644 | if (zprivs_state.vtygrp == (gid_t)-1) { | |
645 | fprintf(stderr, | |
646 | "privs_init: could not lookup vty group %s\n", | |
647 | zprivs->vty_group); | |
648 | exit(1); | |
649 | } | |
650 | ||
651 | for (i = 0; i < ngroups; i++) | |
652 | if (groups[i] == zprivs_state.vtygrp) { | |
653 | found++; | |
654 | break; | |
655 | } | |
656 | ||
657 | if (!found) { | |
658 | fprintf(stderr, | |
659 | "privs_init: user(%s) is not part of vty group specified(%s)\n", | |
660 | zprivs->user, zprivs->vty_group); | |
661 | exit(1); | |
662 | } | |
7e3a1ec7 | 663 | if (i >= ngroups && ngroups < (int)array_size(groups)) { |
d62a17ae | 664 | groups[i] = zprivs_state.vtygrp; |
665 | } | |
666 | } | |
667 | ||
668 | zprivs_state.zsuid = geteuid(); /* initial uid */ | |
669 | /* add groups only if we changed uid - otherwise skip */ | |
670 | if ((ngroups) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
671 | if (setgroups(ngroups, groups)) { | |
672 | fprintf(stderr, "privs_init: could not setgroups, %s\n", | |
673 | safe_strerror(errno)); | |
674 | exit(1); | |
675 | } | |
676 | } | |
677 | ||
678 | /* change gid only if we changed uid - otherwise skip */ | |
679 | if ((zprivs_state.zgid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
680 | /* change group now, forever. uid we do later */ | |
681 | if (setregid(zprivs_state.zgid, zprivs_state.zgid)) { | |
682 | fprintf(stderr, "zprivs_init: could not setregid, %s\n", | |
683 | safe_strerror(errno)); | |
684 | exit(1); | |
685 | } | |
686 | } | |
687 | ||
ceacedba | 688 | #ifdef HAVE_CAPABILITIES |
d62a17ae | 689 | zprivs_caps_init(zprivs); |
8e04538c DS |
690 | |
691 | /* | |
692 | * If we have initialized the system with no requested | |
693 | * capabilities, change will not have been set | |
694 | * to anything by zprivs_caps_init, As such | |
695 | * we should make sure that when we attempt | |
696 | * to raize privileges that we actually have | |
697 | * a do nothing function to call instead of a | |
698 | * crash :). | |
699 | */ | |
700 | if (!zprivs->change) | |
701 | zprivs->change = zprivs_change_null; | |
702 | ||
d62a17ae | 703 | #else /* !HAVE_CAPABILITIES */ |
704 | /* we dont have caps. we'll need to maintain rid and saved uid | |
485ac9a7 | 705 | * and change euid back to saved uid (who we presume has all necessary |
d62a17ae | 706 | * privileges) whenever we are asked to raise our privileges. |
707 | * | |
708 | * This is not worth that much security wise, but all we can do. | |
709 | */ | |
710 | zprivs_state.zsuid = geteuid(); | |
711 | /* only change uid if we don't have the correct one */ | |
712 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
713 | if (setreuid(-1, zprivs_state.zuid)) { | |
714 | fprintf(stderr, | |
715 | "privs_init (uid): could not setreuid, %s\n", | |
716 | safe_strerror(errno)); | |
717 | exit(1); | |
718 | } | |
719 | } | |
720 | ||
721 | zprivs->change = zprivs_change_uid; | |
722 | zprivs->current_state = zprivs_state_uid; | |
ceacedba | 723 | #endif /* HAVE_CAPABILITIES */ |
01245821 | 724 | } |
725 | ||
d62a17ae | 726 | void zprivs_terminate(struct zebra_privs_t *zprivs) |
01245821 | 727 | { |
8875d051 MS |
728 | struct zebra_privs_refs_t *refs; |
729 | ||
877057b6 DL |
730 | lib_privs = NULL; |
731 | ||
d62a17ae | 732 | if (!zprivs) { |
733 | fprintf(stderr, "%s: no privs struct given, terminating", | |
734 | __func__); | |
735 | exit(0); | |
736 | } | |
737 | ||
ceacedba | 738 | #ifdef HAVE_CAPABILITIES |
4093d47b DL |
739 | if (zprivs->user || zprivs->group || zprivs->cap_num_p |
740 | || zprivs->cap_num_i) | |
741 | zprivs_caps_terminate(); | |
d62a17ae | 742 | #else /* !HAVE_CAPABILITIES */ |
743 | /* only change uid if we don't have the correct one */ | |
744 | if ((zprivs_state.zuid) && (zprivs_state.zsuid != zprivs_state.zuid)) { | |
745 | if (setreuid(zprivs_state.zuid, zprivs_state.zuid)) { | |
746 | fprintf(stderr, | |
747 | "privs_terminate: could not setreuid, %s", | |
748 | safe_strerror(errno)); | |
749 | exit(1); | |
750 | } | |
751 | } | |
01245821 | 752 | #endif /* HAVE_LCAPS */ |
ceacedba | 753 | |
8875d051 MS |
754 | while ((refs = STAILQ_FIRST(&(zprivs->thread_refs))) != NULL) { |
755 | STAILQ_REMOVE_HEAD(&(zprivs->thread_refs), entry); | |
756 | XFREE(MTYPE_PRIVS, refs); | |
757 | } | |
758 | ||
d62a17ae | 759 | zprivs->change = zprivs_change_null; |
760 | zprivs->current_state = zprivs_state_null; | |
761 | zprivs_null_state = ZPRIVS_LOWERED; | |
762 | return; | |
01245821 | 763 | } |
ba3a0bc5 | 764 | |
d62a17ae | 765 | void zprivs_get_ids(struct zprivs_ids_t *ids) |
ba3a0bc5 | 766 | { |
767 | ||
d62a17ae | 768 | ids->uid_priv = getuid(); |
769 | (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) | |
e117b7c5 | 770 | : (ids->uid_normal = (uid_t)-1); |
d62a17ae | 771 | (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) |
e117b7c5 | 772 | : (ids->gid_normal = (uid_t)-1); |
d62a17ae | 773 | (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) |
e117b7c5 | 774 | : (ids->gid_vty = (uid_t)-1); |
d62a17ae | 775 | |
776 | return; | |
ba3a0bc5 | 777 | } |