1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <platform.h>
26
27 #include <cf3.defs.h>
28 #include <acl_tools.h>
29
30 #ifdef HAVE_ACL_H
31 # include <acl.h>
32 #endif
33
34 #ifdef HAVE_SYS_ACL_H
35 # include <sys/acl.h>
36 #endif
37
38 #ifdef HAVE_ACL_LIBACL_H
39 # include <acl/libacl.h>
40 #endif
41
42 #ifdef HAVE_LIBACL
43 #include <pwd.h> /* getpwnam */
44
CopyACLs(const char * src,const char * dst,bool * change)45 bool CopyACLs(const char *src, const char *dst, bool *change)
46 {
47 acl_t acls;
48 struct stat statbuf;
49 int ret;
50
51 acls = acl_get_file(src, ACL_TYPE_ACCESS);
52 if (!acls)
53 {
54 if (errno == ENOTSUP)
55 {
56 if (change != NULL)
57 {
58 *change = false;
59 }
60 return true;
61 }
62 else
63 {
64 Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (acl_get_file: %s)", src, GetErrorStr());
65 return false;
66 }
67 }
68 ret = acl_set_file(dst, ACL_TYPE_ACCESS, acls);
69 acl_free(acls);
70 if (ret != 0)
71 {
72 if (errno == ENOTSUP)
73 {
74 if (change != NULL)
75 {
76 *change = false;
77 }
78 return true;
79 }
80 else
81 {
82 Log(LOG_LEVEL_ERR, "Can't copy ACLs to '%s'. (acl_set_file: %s)", dst, GetErrorStr());
83 return false;
84 }
85 }
86
87 if (stat(src, &statbuf) != 0)
88 {
89 Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (stat: %s)", src, GetErrorStr());
90 return false;
91 }
92 if (!S_ISDIR(statbuf.st_mode))
93 {
94 if (change != NULL)
95 {
96 *change = false;
97 }
98 return true;
99 }
100
101 // For directory, copy default ACL too.
102 acls = acl_get_file(src, ACL_TYPE_DEFAULT);
103 if (!acls)
104 {
105 Log(LOG_LEVEL_ERR, "Can't copy ACLs from '%s'. (acl_get_file: %s)", src, GetErrorStr());
106 return false;
107 }
108 ret = acl_set_file(dst, ACL_TYPE_DEFAULT, acls);
109 acl_free(acls);
110 if (ret != 0)
111 {
112 Log(LOG_LEVEL_ERR, "Can't copy ACLs to '%s'. (acl_set_file: %s)", dst, GetErrorStr());
113 return false;
114 }
115 if (change != NULL)
116 {
117 *change = true;
118 }
119 return true;
120 }
121
AllowAccessForUsers(const char * path,StringSet * users,bool allow_writes,bool allow_execute)122 bool AllowAccessForUsers(const char *path, StringSet *users, bool allow_writes, bool allow_execute)
123 {
124 assert(path != NULL);
125 assert(users != NULL);
126
127 acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
128 if (acl == NULL)
129 {
130 Log(LOG_LEVEL_ERR, "Failed to get ACLs for '%s': %s", path, GetErrorStr());
131 return false;
132 }
133
134 acl_entry_t entry;
135 int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
136 while (ret > 0)
137 {
138 acl_tag_t entry_tag;
139 if (acl_get_tag_type(entry, &entry_tag) == -1)
140 {
141 Log(LOG_LEVEL_ERR, "Failed to get ACL entry type: %s", GetErrorStr());
142 acl_free(acl);
143 return false;
144 }
145 if (entry_tag == ACL_USER)
146 {
147 if (acl_delete_entry(acl, entry) == -1)
148 {
149 Log(LOG_LEVEL_ERR, "Failed to remove user ACL entry: %s", GetErrorStr());
150 acl_free(acl);
151 return false;
152 }
153 }
154 ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
155 }
156 if (ret == -1)
157 {
158 Log(LOG_LEVEL_ERR, "Failed to get ACL entries: %s", GetErrorStr());
159 acl_free(acl);
160 return false;
161 }
162
163 StringSetIterator iter = StringSetIteratorInit(users);
164 const char *user = NULL;
165 while ((user = StringSetIteratorNext(&iter)) != NULL)
166 {
167 struct passwd *pw = getpwnam(user);
168 if (pw == NULL)
169 {
170 if (errno != 0)
171 {
172 Log(LOG_LEVEL_ERR, "Failed to get UID for user '%s': %s", user, GetErrorStr());
173 }
174 else
175 {
176 Log(LOG_LEVEL_ERR, "Failed to get UID for user '%s': user doesn't exist", user);
177 }
178 acl_free(acl);
179 return false;
180 }
181 uid_t uid = pw->pw_uid;
182
183 if (acl_create_entry(&acl, &entry) == -1)
184 {
185 Log(LOG_LEVEL_ERR, "Failed to create a new ACL entry: %s", GetErrorStr());
186 acl_free(acl);
187 return false;
188 }
189 if (acl_set_tag_type(entry, ACL_USER) == -1)
190 {
191 Log(LOG_LEVEL_ERR, "Failed to set ACL entry type: %s", GetErrorStr());
192 acl_free(acl);
193 return false;
194 }
195 if (acl_set_qualifier(entry, &uid) == -1)
196 {
197 Log(LOG_LEVEL_ERR, "Failed to set ACL entry qualifier: %s", GetErrorStr());
198 acl_free(acl);
199 return false;
200 }
201
202 acl_permset_t permset;
203 if (acl_get_permset(entry, &permset) == -1)
204 {
205 Log(LOG_LEVEL_ERR, "Failed to get permset: %s", GetErrorStr());
206 acl_free(acl);
207 return false;
208 }
209 if (acl_clear_perms(permset) == -1)
210 {
211 Log(LOG_LEVEL_ERR, "Failed to clear permset: %s", GetErrorStr());
212 acl_free(acl);
213 return false;
214 }
215
216 if (acl_add_perm(permset, ACL_READ) == -1)
217 {
218 Log(LOG_LEVEL_ERR, "Failed to add read permission to set: %s", GetErrorStr());
219 acl_free(acl);
220 return false;
221 }
222
223 if (allow_writes && (acl_add_perm(permset, ACL_WRITE) == -1))
224 {
225 Log(LOG_LEVEL_ERR, "Failed to add write permission to set: %s", GetErrorStr());
226 acl_free(acl);
227 return false;
228 }
229
230 if (allow_execute && (acl_add_perm(permset, ACL_EXECUTE) == -1))
231 {
232 Log(LOG_LEVEL_ERR, "Failed to add execute permission to set: %s", GetErrorStr());
233 acl_free(acl);
234 return false;
235 }
236
237 if (acl_set_permset(entry, permset) == -1)
238 {
239 Log(LOG_LEVEL_ERR, "Failed to set permset: %s", GetErrorStr());
240 acl_free(acl);
241 return false;
242 }
243 }
244
245 if (acl_calc_mask(&acl) == -1)
246 {
247 Log(LOG_LEVEL_ERR, "Failed to recalculate mask: %s", GetErrorStr());
248 acl_free(acl);
249 return false;
250 }
251
252 if (acl_valid(acl) != 0)
253 {
254 Log(LOG_LEVEL_ERR, "Ended up with an invalid ACL");
255 acl_free(acl);
256 return false;
257 }
258
259 if (acl_set_file(path, ACL_TYPE_ACCESS, acl) == -1)
260 {
261 Log(LOG_LEVEL_ERR, "Failed to set ACL: %s", GetErrorStr());
262 acl_free(acl);
263 return false;
264 }
265
266 acl_free(acl);
267 return true;
268 }
269
270 #elif !defined(__MINGW32__) /* !HAVE_LIBACL */
271
CopyACLs(ARG_UNUSED const char * src,ARG_UNUSED const char * dst,bool * change)272 bool CopyACLs(ARG_UNUSED const char *src, ARG_UNUSED const char *dst, bool *change)
273 {
274 if (change != NULL)
275 {
276 *change = false;
277 }
278 return true;
279 }
280
AllowAccessForUsers(ARG_UNUSED const char * path,ARG_UNUSED StringSet * users,ARG_UNUSED bool allow_writes,ARG_UNUSED bool allow_execute)281 bool AllowAccessForUsers(ARG_UNUSED const char *path, ARG_UNUSED StringSet *users,
282 ARG_UNUSED bool allow_writes, ARG_UNUSED bool allow_execute)
283 {
284 Log(LOG_LEVEL_ERR, "ACL manipulation not supported");
285 return false;
286 }
287 #endif
288