1 /*
2 * ProFTPD - mod_sftp miscellaneous
3 * Copyright (c) 2010-2020 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "mod_sftp.h"
26 #include "misc.h"
27
sftp_misc_chown_file(pool * p,pr_fh_t * fh)28 int sftp_misc_chown_file(pool *p, pr_fh_t *fh) {
29 struct stat st;
30 int res, xerrno;
31
32 if (fh == NULL) {
33 errno = EINVAL;
34 return -1;
35 }
36
37 /* session.fsgid defaults to -1, so chown(2) won't chgrp unless specifically
38 * requested via GroupOwner.
39 */
40 if (session.fsuid != (uid_t) -1) {
41 PRIVS_ROOT
42 res = pr_fsio_fchown(fh, session.fsuid, session.fsgid);
43 xerrno = errno;
44 PRIVS_RELINQUISH
45
46 if (res < 0) {
47 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
48 "chown(%s) as root failed: %s", fh->fh_path, strerror(xerrno));
49
50 } else {
51 if (session.fsgid != (gid_t) -1) {
52 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
53 "root chown(%s) to UID %s, GID %s successful", fh->fh_path,
54 pr_uid2str(p, session.fsuid), pr_gid2str(p, session.fsgid));
55
56 } else {
57 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
58 "root chown(%s) to UID %s successful", fh->fh_path,
59 pr_uid2str(NULL, session.fsuid));
60 }
61
62 if (pr_fsio_fstat(fh, &st) < 0) {
63 pr_log_debug(DEBUG0,
64 "'%s' fstat(2) error for root chmod: %s", fh->fh_path,
65 strerror(errno));
66 }
67
68 /* The chmod happens after the chown because chown will remove the
69 * S{U,G}ID bits on some files (namely, directories); the subsequent
70 * chmod is used to restore those dropped bits. This makes it necessary
71 * to use root privs when doing the chmod as well (at least in the case
72 * of chown'ing the file via root privs) in order to ensure that the mode
73 * can be set (a file might be being "given away", and if root privs
74 * aren't used, the chmod() will fail because the old owner/session user
75 * doesn't have the necessary privileges to do so).
76 */
77 PRIVS_ROOT
78 res = pr_fsio_fchmod(fh, st.st_mode);
79 xerrno = errno;
80 PRIVS_RELINQUISH
81
82 if (res < 0) {
83 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
84 "root chmod(%s) to %04o failed: %s", fh->fh_path,
85 (unsigned int) st.st_mode, strerror(xerrno));
86
87 } else {
88 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
89 "root chmod(%s) to %04o successful", fh->fh_path,
90 (unsigned int) st.st_mode);
91 }
92 }
93
94 } else if (session.fsgid != (gid_t) -1) {
95 register unsigned int i;
96 int use_root_privs = TRUE;
97
98 /* Check if session.fsgid is in session.gids. If not, use root privs. */
99 for (i = 0; i < session.gids->nelts; i++) {
100 gid_t *group_ids = session.gids->elts;
101
102 if (group_ids[i] == session.fsgid) {
103 use_root_privs = FALSE;
104 break;
105 }
106 }
107
108 if (use_root_privs) {
109 PRIVS_ROOT
110 }
111
112 res = pr_fsio_fchown(fh, (uid_t) -1, session.fsgid);
113 xerrno = errno;
114
115 if (use_root_privs) {
116 PRIVS_RELINQUISH
117 }
118
119 if (res < 0) {
120 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
121 "%schown(%s) failed: %s", use_root_privs ? "root " : "", fh->fh_path,
122 strerror(xerrno));
123
124 } else {
125 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
126 "%schown(%s) to GID %s successful",
127 use_root_privs ? "root " : "", fh->fh_path,
128 pr_gid2str(NULL, session.fsgid));
129
130 if (pr_fsio_fstat(fh, &st) < 0) {
131 pr_log_debug(DEBUG0,
132 "'%s' fstat(2) error for %sfchmod: %s", fh->fh_path,
133 use_root_privs ? "root " : "", strerror(errno));
134 }
135
136 if (use_root_privs) {
137 PRIVS_ROOT
138 }
139
140 res = pr_fsio_fchmod(fh, st.st_mode);
141 xerrno = errno;
142
143 if (use_root_privs) {
144 PRIVS_RELINQUISH
145 }
146
147 if (res < 0) {
148 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
149 "%schmod(%s) to %04o failed: %s", use_root_privs ? "root " : "",
150 fh->fh_path, (unsigned int) st.st_mode, strerror(xerrno));
151 }
152 }
153 }
154
155 return 0;
156 }
157
sftp_misc_chown_path(pool * p,const char * path)158 int sftp_misc_chown_path(pool *p, const char *path) {
159 struct stat st;
160 int res, xerrno;
161
162 if (path == NULL) {
163 errno = EINVAL;
164 return -1;
165 }
166
167 /* session.fsgid defaults to -1, so chown(2) won't chgrp unless specifically
168 * requested via GroupOwner.
169 */
170 if (session.fsuid != (uid_t) -1) {
171
172 PRIVS_ROOT
173 res = pr_fsio_lchown(path, session.fsuid, session.fsgid);
174 xerrno = errno;
175 PRIVS_RELINQUISH
176
177 if (res < 0) {
178 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
179 "lchown(%s) as root failed: %s", path, strerror(xerrno));
180
181 } else {
182 if (session.fsgid != (gid_t) -1) {
183 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
184 "root lchown(%s) to UID %s, GID %s successful", path,
185 pr_uid2str(p, session.fsuid), pr_gid2str(p, session.fsgid));
186
187 } else {
188 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
189 "root lchown(%s) to UID %s successful", path,
190 pr_uid2str(NULL, session.fsuid));
191 }
192
193 pr_fs_clear_cache2(path);
194 if (pr_fsio_stat(path, &st) < 0) {
195 pr_log_debug(DEBUG0,
196 "'%s' stat(2) error for root chmod: %s", path, strerror(errno));
197 }
198
199 /* The chmod happens after the chown because chown will remove the
200 * S{U,G}ID bits on some files (namely, directories); the subsequent
201 * chmod is used to restore those dropped bits. This makes it necessary
202 * to use root privs when doing the chmod as well (at least in the case
203 * of chown'ing the file via root privs) in order to ensure that the mode
204 * can be set (a file might be being "given away", and if root privs
205 * aren't used, the chmod() will fail because the old owner/session user
206 * doesn't have the necessary privileges to do so).
207 */
208 PRIVS_ROOT
209 res = pr_fsio_chmod(path, st.st_mode);
210 xerrno = errno;
211 PRIVS_RELINQUISH
212
213 if (res < 0) {
214 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
215 "root chmod(%s) to %04o failed: %s", path,
216 (unsigned int) st.st_mode, strerror(xerrno));
217
218 } else {
219 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
220 "root chmod(%s) to %04o successful", path,
221 (unsigned int) st.st_mode);
222 }
223 }
224
225 } else if (session.fsgid != (gid_t) -1) {
226 register unsigned int i;
227 int use_root_privs = TRUE;
228
229 /* Check if session.fsgid is in session.gids. If not, use root privs. */
230 for (i = 0; i < session.gids->nelts; i++) {
231 gid_t *group_ids = session.gids->elts;
232
233 if (group_ids[i] == session.fsgid) {
234 use_root_privs = FALSE;
235 break;
236 }
237 }
238
239 if (use_root_privs) {
240 PRIVS_ROOT
241 }
242
243 res = pr_fsio_lchown(path, (uid_t) -1, session.fsgid);
244 xerrno = errno;
245
246 if (use_root_privs) {
247 PRIVS_RELINQUISH
248 }
249
250 if (res < 0) {
251 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
252 "%slchown(%s) failed: %s", use_root_privs ? "root " : "", path,
253 strerror(xerrno));
254
255 } else {
256 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
257 "%slchown(%s) to GID %s successful",
258 use_root_privs ? "root " : "", path,
259 pr_gid2str(NULL, session.fsgid));
260
261 pr_fs_clear_cache2(path);
262 if (pr_fsio_stat(path, &st) < 0) {
263 pr_log_debug(DEBUG0,
264 "'%s' stat(2) error for %schmod: %s", path,
265 use_root_privs ? "root " : "", strerror(errno));
266 }
267
268 if (use_root_privs) {
269 PRIVS_ROOT
270 }
271
272 res = pr_fsio_chmod(path, st.st_mode);
273 xerrno = errno;
274
275 if (use_root_privs) {
276 PRIVS_RELINQUISH
277 }
278
279 if (res < 0) {
280 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
281 "%schmod(%s) to %04o failed: %s", use_root_privs ? "root " : "",
282 path, (unsigned int) st.st_mode, strerror(xerrno));
283 }
284 }
285 }
286
287 return 0;
288 }
289
sftp_misc_get_chroot(pool * p)290 const char *sftp_misc_get_chroot(pool *p) {
291 return pr_table_get(session.notes, "mod_sftp.chroot-path", NULL);
292 }
293
sftp_misc_namelist_contains(pool * p,const char * namelist,const char * name)294 int sftp_misc_namelist_contains(pool *p, const char *namelist,
295 const char *name) {
296 register unsigned int i;
297 int res = FALSE;
298 pool *tmp_pool;
299 array_header *list;
300 const char **elts;
301
302 tmp_pool = make_sub_pool(p);
303 pr_pool_tag(tmp_pool, "Contains name pool");
304
305 list = pr_str_text_to_array(tmp_pool, namelist, ',');
306 elts = (const char **) list->elts;
307
308 for (i = 0; i < list->nelts; i++) {
309 if (strcmp(elts[i], name) == 0) {
310 res = TRUE;
311 break;
312 }
313 }
314
315 destroy_pool(tmp_pool);
316 return res;
317 }
318
sftp_misc_namelist_shared(pool * p,const char * c2s_names,const char * s2c_names)319 const char *sftp_misc_namelist_shared(pool *p, const char *c2s_names,
320 const char *s2c_names) {
321 register unsigned int i;
322 const char *name = NULL, **client_names, **server_names;
323 pool *tmp_pool;
324 array_header *client_list, *server_list;
325
326 tmp_pool = make_sub_pool(p);
327 pr_pool_tag(tmp_pool, "Share name pool");
328
329 client_list = pr_str_text_to_array(tmp_pool, c2s_names, ',');
330 client_names = (const char **) client_list->elts;
331
332 server_list = pr_str_text_to_array(tmp_pool, s2c_names, ',');
333 server_names = (const char **) server_list->elts;
334
335 for (i = 0; i < client_list->nelts; i++) {
336 register unsigned int j;
337
338 if (name != NULL) {
339 break;
340 }
341
342 for (j = 0; j < server_list->nelts; j++) {
343 if (strcmp(client_names[i], server_names[j]) == 0) {
344 name = client_names[i];
345 break;
346 }
347 }
348 }
349
350 name = pstrdup(p, name);
351 destroy_pool(tmp_pool);
352
353 return name;
354 }
355
356 /* If we are chrooted, AND mod_vroot is present, then abs_path will not
357 * actually point to the real absolute path on disk. Try to account for
358 * this.
359 */
sftp_misc_vroot_abs_path(pool * p,const char * path,int interpolate)360 char *sftp_misc_vroot_abs_path(pool *p, const char *path, int interpolate) {
361 const char *curr_chroot_path, *real_chroot_path;
362 char *abs_path;
363
364 curr_chroot_path = session.chroot_path;
365 real_chroot_path = sftp_misc_get_chroot(p);
366
367 if (real_chroot_path != NULL &&
368 pr_module_exists("mod_vroot.c") == TRUE) {
369 session.chroot_path = real_chroot_path;
370 }
371
372 abs_path = dir_abs_path(p, path, interpolate);
373 session.chroot_path = curr_chroot_path;
374
375 return abs_path;
376 }
377