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