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