1 /*
2  * Part of Very Secure FTPd
3  * Licence: GPL v2
4  * Author: Chris Evans
5  * ftppolicy.c
6  *
7  * Code to build a sandbox policy based on current session options.
8  */
9 
10 #include "ftppolicy.h"
11 #include "ptracesandbox.h"
12 #include "tunables.h"
13 #include "session.h"
14 #include "sysutil.h"
15 
16 /* For AF_INET etc. network constants. */
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in_systm.h>
20 #include <netinet/in.h>
21 #include <netinet/ip.h>
22 #include <netinet/tcp.h>
23 
24 static int socket_validator(struct pt_sandbox* p_sandbox, void* p_arg);
25 static int connect_validator(struct pt_sandbox* p_sandbox, void* p_arg);
26 static int getsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg);
27 static int setsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg);
28 
29 void
policy_setup(struct pt_sandbox * p_sandbox,const struct vsf_session * p_sess)30 policy_setup(struct pt_sandbox* p_sandbox, const struct vsf_session* p_sess)
31 {
32   int is_anon = p_sess->is_anonymous;
33   /* Always need to be able to exit! */
34   ptrace_sandbox_permit_exit(p_sandbox);
35   /* Needed for memory management. */
36   ptrace_sandbox_permit_mmap(p_sandbox);
37   ptrace_sandbox_permit_mprotect(p_sandbox);
38   ptrace_sandbox_permit_brk(p_sandbox);
39   /* Simple reads and writes are required. Permitting write does not imply
40    * filesystem write access because access control is done at open time.
41    */
42   ptrace_sandbox_permit_read(p_sandbox);
43   ptrace_sandbox_permit_write(p_sandbox);
44   /* Reading FTP commands from the network. */
45   ptrace_sandbox_permit_recv(p_sandbox);
46   /* Querying time is harmless; used for log timestamps and internally to
47    * OpenSSL
48    */
49   ptrace_sandbox_permit_query_time(p_sandbox);
50 
51   /* Typically post-login things follow. */
52   /* Since we're in a chroot(), we can just blanket allow filesystem readonly
53    * open.
54    */
55   ptrace_sandbox_permit_open(p_sandbox, 0);
56   ptrace_sandbox_permit_close(p_sandbox);
57   /* Other pathname-based metadata queries. */
58   ptrace_sandbox_permit_file_stats(p_sandbox);
59   ptrace_sandbox_permit_readlink(p_sandbox);
60   /* Querying, reading and changing directory. */
61   ptrace_sandbox_permit_getcwd(p_sandbox);
62   ptrace_sandbox_permit_chdir(p_sandbox);
63   ptrace_sandbox_permit_getdents(p_sandbox);
64   /* Simple fd-based operations. */
65   ptrace_sandbox_permit_fd_stats(p_sandbox);
66   ptrace_sandbox_permit_seek(p_sandbox);
67   ptrace_sandbox_permit_shutdown(p_sandbox);
68   ptrace_sandbox_permit_fcntl(p_sandbox);
69   ptrace_sandbox_permit_setsockopt(p_sandbox);
70   ptrace_sandbox_set_setsockopt_validator(p_sandbox, setsockopt_validator, 0);
71   /* Misc */
72   /* Setting umask. */
73   ptrace_sandbox_permit_umask(p_sandbox);
74   /* Select for data connection readyness. */
75   ptrace_sandbox_permit_select(p_sandbox);
76   /* Always need ability to take signals (SIGPIPE) */
77   ptrace_sandbox_permit_sigreturn(p_sandbox);
78   /* Sleeping (bandwidth limit, connect retires, anon login fails) */
79   ptrace_sandbox_permit_sleep(p_sandbox);
80   /* High-speed transfers... */
81   ptrace_sandbox_permit_sendfile(p_sandbox);
82   /* TODO - Grrrr! nscd cache access is leaking into child. Need to find out
83    * out how to disable that. Also means that text_userdb_names loads values
84    * from the real system data.
85    */
86   if (tunable_text_userdb_names)
87   {
88     ptrace_sandbox_permit_mremap(p_sandbox);
89   }
90   /* May need ability to install signal handlers. */
91   if (tunable_async_abor_enable ||
92       tunable_idle_session_timeout > 0 ||
93       tunable_data_connection_timeout > 0)
94   {
95     ptrace_sandbox_permit_sigaction(p_sandbox);
96   }
97   /* May need ability to set up timeout alarms. */
98   if (tunable_idle_session_timeout > 0 || tunable_data_connection_timeout > 0)
99   {
100     ptrace_sandbox_permit_alarm(p_sandbox);
101   }
102   /* Set up network permissions according to config and session. */
103   ptrace_sandbox_permit_socket(p_sandbox);
104   ptrace_sandbox_set_socket_validator(p_sandbox,
105                                       socket_validator,
106                                       (void*) p_sess);
107   ptrace_sandbox_permit_bind(p_sandbox);
108   /* Yes, reuse of the connect validator is intentional. */
109   ptrace_sandbox_set_bind_validator(p_sandbox,
110                                     connect_validator,
111                                     (void*) p_sess);
112   if (tunable_port_enable)
113   {
114     ptrace_sandbox_permit_connect(p_sandbox);
115     ptrace_sandbox_set_connect_validator(p_sandbox,
116                                          connect_validator,
117                                          (void*) p_sess);
118     ptrace_sandbox_permit_getsockopt(p_sandbox);
119     ptrace_sandbox_set_getsockopt_validator(p_sandbox, getsockopt_validator, 0);
120   }
121   if (tunable_pasv_enable)
122   {
123     ptrace_sandbox_permit_listen(p_sandbox);
124     ptrace_sandbox_permit_accept(p_sandbox);
125   }
126   /* Set up write permissions according to config and session. */
127   if (tunable_write_enable)
128   {
129     if (!is_anon || tunable_anon_upload_enable)
130     {
131       ptrace_sandbox_permit_open(p_sandbox, 1);
132     }
133     if (!is_anon || tunable_anon_mkdir_write_enable)
134     {
135       ptrace_sandbox_permit_mkdir(p_sandbox);
136     }
137     if (!is_anon || tunable_anon_other_write_enable)
138     {
139       ptrace_sandbox_permit_unlink(p_sandbox);
140       ptrace_sandbox_permit_rmdir(p_sandbox);
141       ptrace_sandbox_permit_rename(p_sandbox);
142       ptrace_sandbox_permit_ftruncate(p_sandbox);
143       if (tunable_mdtm_write)
144       {
145         ptrace_sandbox_permit_utime(p_sandbox);
146       }
147     }
148     if (!is_anon && tunable_chmod_enable)
149     {
150       ptrace_sandbox_permit_chmod(p_sandbox);
151     }
152     if (is_anon && tunable_chown_uploads)
153     {
154       ptrace_sandbox_permit_fchmod(p_sandbox);
155       ptrace_sandbox_permit_fchown(p_sandbox);
156     }
157   }
158 }
159 
160 static int
socket_validator(struct pt_sandbox * p_sandbox,void * p_arg)161 socket_validator(struct pt_sandbox* p_sandbox, void* p_arg)
162 {
163   int ret;
164   struct vsf_session* p_sess = (struct vsf_session*) p_arg;
165   unsigned long arg1;
166   unsigned long arg2;
167   unsigned long expected_family = AF_INET;
168   if (vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr))
169   {
170     expected_family = AF_INET6;
171   }
172   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 0, &arg1);
173   if (ret != 0)
174   {
175     return ret;
176   }
177   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
178   if (ret != 0)
179   {
180     return ret;
181   }
182   if (arg1 != expected_family || arg2 != SOCK_STREAM)
183   {
184     return -1;
185   }
186   return 0;
187 }
188 
189 static int
connect_validator(struct pt_sandbox * p_sandbox,void * p_arg)190 connect_validator(struct pt_sandbox* p_sandbox, void* p_arg)
191 {
192   int ret;
193   struct vsf_session* p_sess = (struct vsf_session*) p_arg;
194   unsigned long arg2;
195   unsigned long arg3;
196   unsigned long expected_family = AF_INET;
197   unsigned long expected_len = sizeof(struct sockaddr_in);
198   void* p_buf = 0;
199   struct sockaddr* p_sockaddr;
200   static struct vsf_sysutil_sockaddr* p_sockptr;
201   if (vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr))
202   {
203     expected_family = AF_INET6;
204     expected_len = sizeof(struct sockaddr_in6);
205   }
206   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
207   if (ret != 0)
208   {
209     return ret;
210   }
211   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
212   if (ret != 0)
213   {
214     return ret;
215   }
216   if (arg3 != expected_len)
217   {
218     return -1;
219   }
220   p_buf = vsf_sysutil_malloc((int) expected_len);
221   ret = ptrace_sandbox_get_buf(p_sandbox, arg2, expected_len, p_buf);
222   if (ret != 0)
223   {
224     vsf_sysutil_free(p_buf);
225     return -2;
226   }
227   p_sockaddr = (struct sockaddr*) p_buf;
228   if (p_sockaddr->sa_family != expected_family)
229   {
230     vsf_sysutil_free(p_buf);
231     return -3;
232   }
233   if (expected_family == AF_INET)
234   {
235     struct sockaddr_in* p_sockaddr_in = (struct sockaddr_in*) p_sockaddr;
236     vsf_sysutil_sockaddr_alloc_ipv4(&p_sockptr);
237     vsf_sysutil_sockaddr_set_ipv4addr(p_sockptr,
238                                       (const unsigned char*)
239                                           &p_sockaddr_in->sin_addr);
240   }
241   else
242   {
243     struct sockaddr_in6* p_sockaddr_in6 = (struct sockaddr_in6*) p_sockaddr;
244     vsf_sysutil_sockaddr_alloc_ipv6(&p_sockptr);
245     vsf_sysutil_sockaddr_set_ipv6addr(p_sockptr,
246                                       (const unsigned char*)
247                                           &p_sockaddr_in6->sin6_addr);
248   }
249   if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_sockptr))
250   {
251     vsf_sysutil_free(p_buf);
252     return -4;
253   }
254   vsf_sysutil_free(p_buf);
255   return 0;
256 }
257 
258 static int
getsockopt_validator(struct pt_sandbox * p_sandbox,void * p_arg)259 getsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg)
260 {
261   int ret;
262   unsigned long arg2;
263   unsigned long arg3;
264   (void) p_arg;
265   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
266   if (ret != 0)
267   {
268     return ret;
269   }
270   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
271   if (ret != 0)
272   {
273     return ret;
274   }
275   if (arg2 != SOL_SOCKET || arg3 != SO_ERROR)
276   {
277     return -1;
278   }
279   return 0;
280 }
281 
282 static int
setsockopt_validator(struct pt_sandbox * p_sandbox,void * p_arg)283 setsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg)
284 {
285   int ret;
286   unsigned long arg2;
287   unsigned long arg3;
288   (void) p_arg;
289   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
290   if (ret != 0)
291   {
292     return ret;
293   }
294   ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
295   if (ret != 0)
296   {
297     return ret;
298   }
299   if (arg2 == SOL_SOCKET)
300   {
301     if (arg3 != SO_KEEPALIVE &&
302         arg3 != SO_REUSEADDR &&
303         arg3 != SO_OOBINLINE &&
304         arg3 != SO_LINGER)
305     {
306       return -1;
307     }
308   }
309   else if (arg2 == IPPROTO_TCP)
310   {
311     if (arg3 != TCP_NODELAY)
312     {
313       return -2;
314     }
315   }
316   else if (arg2 == IPPROTO_IP)
317   {
318     if (arg3 != IP_TOS)
319     {
320       return -3;
321     }
322   }
323   else
324   {
325     return -4;
326   }
327   return 0;
328 }
329