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