1 /*
2  * Copyright (c) 2012 Balabit
3  * Copyright (c) 2012 Balazs Scheidler <bazsi@balabit.hu>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 
25 #include "file-perms.h"
26 #include "userdb.h"
27 #include "messages.h"
28 #include "cfg.h"
29 #include "gprocess.h"
30 
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 #include <errno.h>
35 
36 #define DONTCHANGE -2
37 
38 void
file_perm_options_set_file_uid(FilePermOptions * self,const gchar * file_uid)39 file_perm_options_set_file_uid(FilePermOptions *self, const gchar *file_uid)
40 {
41   self->file_uid = 0;
42   if (!resolve_user(file_uid, &self->file_uid))
43     {
44       msg_error("Error resolving user",
45                 evt_tag_str("user", file_uid));
46     }
47 }
48 
49 void
file_perm_options_dont_change_file_uid(FilePermOptions * self)50 file_perm_options_dont_change_file_uid(FilePermOptions *self)
51 {
52   self->file_uid = DONTCHANGE;
53 }
54 
55 void
file_perm_options_set_file_gid(FilePermOptions * self,const gchar * file_gid)56 file_perm_options_set_file_gid(FilePermOptions *self, const gchar *file_gid)
57 {
58   self->file_gid = 0;
59   if (!resolve_group(file_gid, &self->file_gid))
60     {
61       msg_error("Error resolving group",
62                 evt_tag_str("group", file_gid));
63     }
64 }
65 
66 void
file_perm_options_dont_change_file_gid(FilePermOptions * self)67 file_perm_options_dont_change_file_gid(FilePermOptions *self)
68 {
69   self->file_gid = DONTCHANGE;
70 }
71 
72 void
file_perm_options_set_file_perm(FilePermOptions * self,gint file_perm)73 file_perm_options_set_file_perm(FilePermOptions *self, gint file_perm)
74 {
75   self->file_perm = file_perm;
76 }
77 
78 void
file_perm_options_dont_change_file_perm(FilePermOptions * self)79 file_perm_options_dont_change_file_perm(FilePermOptions *self)
80 {
81   self->file_perm = DONTCHANGE;
82 }
83 
84 void
file_perm_options_set_dir_uid(FilePermOptions * self,const gchar * dir_uid)85 file_perm_options_set_dir_uid(FilePermOptions *self, const gchar *dir_uid)
86 {
87   self->dir_uid = 0;
88   if (!resolve_user(dir_uid, &self->dir_uid))
89     {
90       msg_error("Error resolving user",
91                 evt_tag_str("user", dir_uid));
92     }
93 }
94 
95 void
file_perm_options_dont_change_dir_uid(FilePermOptions * self)96 file_perm_options_dont_change_dir_uid(FilePermOptions *self)
97 {
98   self->dir_uid = DONTCHANGE;
99 }
100 
101 void
file_perm_options_set_dir_gid(FilePermOptions * self,const gchar * dir_gid)102 file_perm_options_set_dir_gid(FilePermOptions *self, const gchar *dir_gid)
103 {
104   self->dir_gid = 0;
105   if (!resolve_group(dir_gid, &self->dir_gid))
106     {
107       msg_error("Error resolving group",
108                 evt_tag_str("group", dir_gid));
109     }
110 }
111 
112 void
file_perm_options_dont_change_dir_gid(FilePermOptions * self)113 file_perm_options_dont_change_dir_gid(FilePermOptions *self)
114 {
115   self->dir_gid = DONTCHANGE;
116 }
117 
118 void
file_perm_options_set_dir_perm(FilePermOptions * self,gint dir_perm)119 file_perm_options_set_dir_perm(FilePermOptions *self, gint dir_perm)
120 {
121   self->dir_perm = dir_perm;
122 }
123 
124 void
file_perm_options_dont_change_dir_perm(FilePermOptions * self)125 file_perm_options_dont_change_dir_perm(FilePermOptions *self)
126 {
127   self->dir_perm = DONTCHANGE;
128 }
129 
130 void
file_perm_options_defaults(FilePermOptions * self)131 file_perm_options_defaults(FilePermOptions *self)
132 {
133   self->file_uid = self->file_gid = -1;
134   self->file_perm = -1;
135   self->dir_uid = self->dir_gid = -1;
136   self->dir_perm = -1;
137 }
138 
139 void
file_perm_options_global_defaults(FilePermOptions * self)140 file_perm_options_global_defaults(FilePermOptions *self)
141 {
142   self->file_uid = self->file_gid = -1;
143   self->file_perm = 0600;
144   self->dir_uid = self->dir_gid = -1;
145   self->dir_perm = 0700;
146 }
147 
148 void
file_perm_options_inherit_from(FilePermOptions * self,const FilePermOptions * from)149 file_perm_options_inherit_from(FilePermOptions *self, const FilePermOptions *from)
150 {
151   if (self->file_uid == -1)
152     self->file_uid = from->file_uid;
153   if (self->file_gid == -1)
154     self->file_gid = from->file_gid;
155   if (self->file_perm == -1)
156     self->file_perm = from->file_perm;
157   if (self->dir_uid == -1)
158     self->dir_uid = from->dir_uid;
159   if (self->dir_gid == -1)
160     self->dir_gid = from->dir_gid;
161   if (self->dir_perm == -1)
162     self->dir_perm = from->dir_perm;
163 }
164 
165 void
file_perm_options_inherit_dont_change(FilePermOptions * self)166 file_perm_options_inherit_dont_change(FilePermOptions *self)
167 {
168   FilePermOptions dont_change =
169   {
170     .file_uid = DONTCHANGE,
171     .file_gid = DONTCHANGE,
172     .file_perm = DONTCHANGE,
173     .dir_uid = DONTCHANGE,
174     .dir_gid = DONTCHANGE,
175     .dir_perm = DONTCHANGE,
176   };
177 
178   file_perm_options_inherit_from(self, &dont_change);
179 }
180 
181 gboolean
file_perm_options_apply_file(const FilePermOptions * self,const gchar * path)182 file_perm_options_apply_file(const FilePermOptions *self, const gchar *path)
183 {
184 #ifndef _MSC_VER
185   gboolean result = TRUE;
186 
187   if (self->file_uid >= 0 && chown(path, (uid_t) self->file_uid, -1) < 0)
188     result = FALSE;
189   if (self->file_gid >= 0 && chown(path, -1, (gid_t) self->file_gid) < 0)
190     result = FALSE;
191   if (self->file_perm >= 0 && chmod(path, (mode_t) self->file_perm) < 0)
192     result = FALSE;
193   return result;
194 #endif
195 }
196 
197 gboolean
file_perm_options_apply_dir(const FilePermOptions * self,const gchar * path)198 file_perm_options_apply_dir(const FilePermOptions *self, const gchar *path)
199 {
200 #ifndef _MSC_VER
201   gboolean result = TRUE;
202 
203   if (self->dir_uid >= 0 && chown(path, (uid_t) self->dir_uid, -1) < 0)
204     result = FALSE;
205   if (self->dir_gid >= 0 && chown(path, -1, (gid_t) self->dir_gid) < 0)
206     result = FALSE;
207   if (self->dir_perm >= 0 && chmod(path, (mode_t) self->dir_perm) < 0)
208     result = FALSE;
209   return result;
210 #endif
211 }
212 
213 gboolean
file_perm_options_apply_fd(const FilePermOptions * self,gint fd)214 file_perm_options_apply_fd(const FilePermOptions *self, gint fd)
215 {
216 #ifndef _MSC_VER
217   gboolean result = TRUE;
218 
219   if (self->file_uid >= 0 && fchown(fd, (uid_t) self->file_uid, -1) < 0)
220     result = FALSE;
221   if (self->file_gid >= 0 && fchown(fd, -1, (gid_t) self->file_gid) < 0)
222     result = FALSE;
223   if (self->file_perm >= 0 && fchmod(fd, (mode_t) self->file_perm) < 0)
224     result = FALSE;
225 
226   return result;
227 #endif
228 }
229 
230 /**
231  *
232  * This function receives a complete path (directory + filename) and creates
233  * the directory portion if it does not exist. The point is that the caller
234  * wants to ensure that the given filename can be opened after this function
235  * returns. (at least it won't fail because of missing directories).
236  **/
237 gboolean
file_perm_options_create_containing_directory(const FilePermOptions * self,const gchar * path)238 file_perm_options_create_containing_directory(const FilePermOptions *self, const gchar *path)
239 {
240   gboolean result = FALSE;
241   gchar *_path;
242   gchar *dirname;
243   struct stat st;
244   gint rc;
245   gchar *p;
246   cap_t saved_caps;
247 
248   _path = g_strdup(path);
249 
250   /* check that the directory exists */
251   dirname = g_path_get_dirname(_path);
252   rc = stat(dirname, &st);
253   g_free(dirname);
254 
255   if (rc == 0)
256     {
257       /* directory already exists */
258       result = TRUE;
259       goto finish;
260     }
261   else if (rc < 0 && errno != ENOENT)
262     {
263       /* some real error occurred */
264       result = FALSE;
265       goto finish;
266     }
267 
268   /* directory does not exist */
269 
270   p = strchr(_path + 1, '/');
271   while (p)
272     {
273       *p = 0;
274       if (stat(_path, &st) == 0)
275         {
276           if (!S_ISDIR(st.st_mode))
277             {
278               result = FALSE;
279               goto finish;
280             }
281         }
282       else if (errno == ENOENT)
283         {
284           if (mkdir(_path, self->dir_perm < 0 ? 0700 : (mode_t) self->dir_perm) == -1)
285             {
286               result = FALSE;
287               goto finish;
288             }
289           saved_caps = g_process_cap_save();
290           g_process_enable_cap("cap_chown");
291           g_process_enable_cap("cap_fowner");
292           file_perm_options_apply_dir(self, _path);
293           g_process_cap_restore(saved_caps);
294         }
295       *p = '/';
296       p = strchr(p + 1, '/');
297     }
298 
299   result = TRUE;
300 
301 finish:
302   g_free(_path);
303   return result;
304 }
305