]> git.ipfire.org Git - people/ms/network.git/blob - src/networkd/main.c
networkd: Drop all capabilities except a few we would like to keep
[people/ms/network.git] / src / networkd / main.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2023 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <grp.h>
22 #include <linux/capability.h>
23 #include <pwd.h>
24 #include <stddef.h>
25 #include <sys/capability.h>
26 #include <sys/prctl.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include "daemon.h"
31 #include "logging.h"
32
33 static int cap_acquire_setpcap(void) {
34 cap_flag_value_t value;
35 int r;
36
37 // Fetch current capabilities
38 cap_t caps = cap_get_proc();
39
40 // Check if CAP_SETPCAP is already enabled
41 r = cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &value);
42 if (r) {
43 ERROR("The kernel does not seem to know CAP_SETPCAP: %m\n");
44 goto ERROR;
45 }
46
47 // It CAP_SETPCAP isn't set, we will try to set it
48 if (value != CAP_SET) {
49 const cap_value_t cap = CAP_SETPCAP;
50
51 r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET);
52 if (r) {
53 ERROR("Could not set CAP_SETPCAP: %m\n");
54 goto ERROR;
55 }
56
57 // Store capabilities
58 r = cap_set_proc(caps);
59 if (r) {
60 ERROR("Could not acquire effective CAP_SETPCAP capability: %m\n");
61 goto ERROR;
62 }
63 }
64
65 ERROR:
66 if (caps)
67 cap_free(caps);
68
69 return r;
70 }
71
72 static cap_flag_value_t keep_cap(const cap_value_t cap, const cap_value_t* keep_caps) {
73 for (const cap_value_t* c = keep_caps; *c; c++) {
74 if (cap == *c)
75 return CAP_SET;
76 }
77
78 return CAP_CLEAR;
79 }
80
81 static int drop_capabilities(void) {
82 int r;
83
84 const cap_value_t keep_caps[] = {
85 CAP_NET_ADMIN,
86 CAP_NET_BIND_SERVICE,
87 CAP_NET_BROADCAST,
88 CAP_NET_RAW,
89 0,
90 };
91
92 // Acquire CAP_SETPCAP
93 r = cap_acquire_setpcap();
94 if (r)
95 return r;
96
97 // Fetch the current set of capabilities
98 cap_t caps = cap_get_proc();
99
100 // Drop all capabilities that we do not need
101 for (cap_value_t cap = 1; cap <= CAP_LAST_CAP; cap++) {
102 // Skip any capabilities we would like to skip
103 cap_flag_value_t flag = keep_cap(cap, keep_caps);
104
105 // Drop the capability from the bounding set
106 if (flag == CAP_CLEAR) {
107 r = prctl(PR_CAPBSET_DROP, cap);
108 if (r) {
109 ERROR("Could not drop capability from the bounding set: %m\n");
110 goto ERROR;
111 }
112 }
113
114 r = cap_set_flag(caps, CAP_INHERITABLE, 1, &cap, CAP_CLEAR);
115 if (r) {
116 ERROR("Could not set capability %d: %m\n", (int)cap);
117 goto ERROR;
118 }
119
120 r = cap_set_flag(caps, CAP_PERMITTED, 1, &cap, flag);
121 if (r) {
122 ERROR("Could not set capability %d: %m\n", (int)cap);
123 goto ERROR;
124 }
125
126 r = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
127 if (r) {
128 ERROR("Could not set capability %d: %m\n", (int)cap);
129 goto ERROR;
130 }
131 }
132
133 // Restore capabilities
134 r = cap_set_proc(caps);
135 if (r) {
136 ERROR("Could not restore capabilities: %m\n");
137 goto ERROR;
138 }
139
140 ERROR:
141 if (caps)
142 cap_free(caps);
143
144 return r;
145 }
146
147 static int drop_privileges(const char* user) {
148 struct passwd* passwd = NULL;
149 int r;
150
151 // Fetch the current user
152 uid_t current_uid = getuid();
153
154 // If we have not been launched by root, we will assume that we have already
155 // been launched with a minimal set of privileges.
156 if (current_uid > 0)
157 return 0;
158
159 DEBUG("Dropping privileges...\n");
160
161 // Fetch information about the desired user
162 passwd = getpwnam(user);
163 if (!passwd) {
164 ERROR("Could not find user %s: %m\n", user);
165 return 1;
166 }
167
168 uid_t uid = passwd->pw_uid;
169 gid_t gid = passwd->pw_gid;
170
171 // Change group
172 r = setresgid(gid, gid, gid);
173 if (r) {
174 ERROR("Could not change group to %d: %m\n", gid);
175 return 1;
176 }
177
178 // Set any supplementary groups
179 r = setgroups(0, NULL);
180 if (r) {
181 ERROR("Could not set supplementary groups: %m\n");
182 return 1;
183 }
184
185 // Do not drop any capabilities when we change to the new user
186 r = prctl(PR_SET_KEEPCAPS, 1);
187 if (r) {
188 ERROR("Could not set PR_SET_KEEPCAPS: %m\n");
189 return 1;
190 }
191
192 // Change to the new user
193 r = setresuid(uid, uid, uid);
194 if (r) {
195 ERROR("Could not change user to %d: %m\n", uid);
196 return 1;
197 }
198
199 // Reset PR_SET_KEEPCAPS
200 r = prctl(PR_SET_KEEPCAPS, 0);
201 if (r) {
202 ERROR("Could not set PR_SET_KEEPCAPS: %m\n");
203 return 1;
204 }
205
206 // Drop capabilities
207 r = drop_capabilities();
208 if (r)
209 return r;
210
211 return 0;
212 }
213
214 int main(int argc, char** argv) {
215 struct nw_daemon* daemon = NULL;
216 int r;
217
218 // Drop privileges
219 r = drop_privileges("network");
220 if (r)
221 return r;
222
223 // Create the daemon
224 r = nw_daemon_create(&daemon);
225 if (r)
226 return r;
227
228 // Run the daemon
229 r = nw_daemon_run(daemon);
230
231 // Cleanup
232 if (daemon)
233 nw_daemon_unref(daemon);
234
235 return r;
236 }