]>
Commit | Line | Data |
---|---|---|
d7832d2c KS |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
9 | under the terms of the GNU Lesser General Public License as published by |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
d7832d2c KS |
11 | (at your option) any later version. |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 16 | Lesser General Public License for more details. |
d7832d2c | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
d7832d2c KS |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <assert.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <stdio.h> | |
27 | #include <sys/types.h> | |
28 | #include <stdarg.h> | |
29 | #include <ctype.h> | |
30 | #include <sys/capability.h> | |
31 | #include <sys/prctl.h> | |
32 | ||
33 | #include "macro.h" | |
34 | #include "capability.h" | |
35 | #include "util.h" | |
36 | #include "log.h" | |
37 | ||
38 | int have_effective_cap(int value) { | |
39 | cap_t cap; | |
40 | cap_flag_value_t fv; | |
41 | int r; | |
42 | ||
ec8927ca LP |
43 | cap = cap_get_proc(); |
44 | if (!cap) | |
d7832d2c KS |
45 | return -errno; |
46 | ||
47 | if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) | |
48 | r = -errno; | |
49 | else | |
50 | r = fv == CAP_SET; | |
51 | ||
52 | cap_free(cap); | |
53 | return r; | |
54 | } | |
55 | ||
56 | unsigned long cap_last_cap(void) { | |
57 | static __thread unsigned long saved; | |
58 | static __thread bool valid = false; | |
59 | unsigned long p; | |
60 | ||
61 | if (valid) | |
62 | return saved; | |
63 | ||
64 | p = (unsigned long) CAP_LAST_CAP; | |
65 | ||
66 | if (prctl(PR_CAPBSET_READ, p) < 0) { | |
67 | ||
68 | /* Hmm, look downwards, until we find one that | |
69 | * works */ | |
70 | for (p--; p > 0; p --) | |
71 | if (prctl(PR_CAPBSET_READ, p) >= 0) | |
72 | break; | |
73 | ||
74 | } else { | |
75 | ||
76 | /* Hmm, look upwards, until we find one that doesn't | |
77 | * work */ | |
78 | for (;; p++) | |
79 | if (prctl(PR_CAPBSET_READ, p+1) < 0) | |
80 | break; | |
81 | } | |
82 | ||
83 | saved = p; | |
84 | valid = true; | |
85 | ||
86 | return p; | |
87 | } | |
ec8927ca LP |
88 | |
89 | int capability_bounding_set_drop(uint64_t drop, bool right_now) { | |
90 | unsigned long i; | |
91 | cap_t after_cap = NULL, temp_cap = NULL; | |
92 | cap_flag_value_t fv; | |
93 | int r; | |
94 | ||
95 | /* If we are run as PID 1 we will lack CAP_SETPCAP by default | |
96 | * in the effective set (yes, the kernel drops that when | |
97 | * executing init!), so get it back temporarily so that we can | |
98 | * call PR_CAPBSET_DROP. */ | |
99 | ||
100 | after_cap = cap_get_proc(); | |
101 | if (!after_cap) | |
102 | return -errno; | |
103 | ||
104 | if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) { | |
105 | cap_free(after_cap); | |
106 | return -errno; | |
107 | } | |
108 | ||
109 | if (fv != CAP_SET) { | |
110 | static const cap_value_t v = CAP_SETPCAP; | |
111 | ||
112 | temp_cap = cap_dup(after_cap); | |
113 | if (!temp_cap) { | |
114 | r = -errno; | |
115 | goto finish; | |
116 | } | |
117 | ||
118 | if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { | |
119 | r = -errno; | |
120 | goto finish; | |
121 | } | |
122 | ||
123 | if (cap_set_proc(temp_cap) < 0) { | |
124 | r = -errno; | |
125 | goto finish; | |
126 | } | |
127 | } | |
128 | ||
129 | for (i = 0; i <= cap_last_cap(); i++) { | |
130 | ||
131 | if (drop & ((uint64_t) 1ULL << (uint64_t) i)) { | |
132 | cap_value_t v; | |
133 | ||
134 | /* Drop it from the bounding set */ | |
135 | if (prctl(PR_CAPBSET_DROP, i) < 0) { | |
136 | r = -errno; | |
137 | goto finish; | |
138 | } | |
139 | v = i; | |
140 | ||
141 | /* Also drop it from the inheritable set, so | |
142 | * that anything we exec() loses the | |
143 | * capability for good. */ | |
144 | if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { | |
145 | r = -errno; | |
146 | goto finish; | |
147 | } | |
148 | ||
149 | /* If we shall apply this right now drop it | |
150 | * also from our own capability sets. */ | |
151 | if (right_now) { | |
152 | if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || | |
153 | cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { | |
154 | r = -errno; | |
155 | goto finish; | |
156 | } | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | r = 0; | |
162 | ||
163 | finish: | |
164 | if (temp_cap) | |
165 | cap_free(temp_cap); | |
166 | ||
167 | if (after_cap) { | |
168 | cap_set_proc(after_cap); | |
169 | cap_free(after_cap); | |
170 | } | |
171 | ||
172 | return r; | |
173 | } |