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