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