1 /*
2  * Part of Very Secure FTPd
3  * Licence: GPL v2
4  * Author: Chris Evans
5  * standalone.c
6  *
7  * Code to listen on the network and launch children servants.
8  */
9 
10 #include "standalone.h"
11 
12 #include "parseconf.h"
13 #include "tunables.h"
14 #include "sysutil.h"
15 #include "sysdeputil.h"
16 #include "utility.h"
17 #include "defs.h"
18 #include "hash.h"
19 #include "str.h"
20 #include "ipaddrparse.h"
21 
22 static unsigned int s_children;
23 static struct hash* s_p_ip_count_hash;
24 static struct hash* s_p_pid_ip_hash;
25 static unsigned int s_ipaddr_size;
26 
27 static void handle_sigchld(void*  duff);
28 static void handle_sighup(void*  duff);
29 static void prepare_child(int sockfd);
30 static unsigned int handle_ip_count(void* p_raw_addr);
31 static void drop_ip_count(void* p_raw_addr);
32 
33 static unsigned int hash_ip(unsigned int buckets, void* p_key);
34 static unsigned int hash_pid(unsigned int buckets, void* p_key);
35 
36 struct vsf_client_launch
vsf_standalone_main(void)37 vsf_standalone_main(void)
38 {
39   struct vsf_sysutil_sockaddr* p_accept_addr = 0;
40   int listen_sock = -1;
41   int retval;
42   s_ipaddr_size = vsf_sysutil_get_ipaddr_size();
43   if (tunable_listen && tunable_listen_ipv6)
44   {
45     die("run two copies of vsftpd for IPv4 and IPv6");
46   }
47   if (tunable_background)
48   {
49     int forkret = vsf_sysutil_fork();
50     if (forkret > 0)
51     {
52       /* Parent, just exit */
53       vsf_sysutil_exit(0);
54     }
55     /* Son, close standard FDs to avoid SSH hang-on-exit */
56     vsf_sysutil_reopen_standard_fds();
57     vsf_sysutil_make_session_leader();
58   }
59   if (tunable_listen)
60   {
61     listen_sock = vsf_sysutil_get_ipv4_sock();
62   }
63   else
64   {
65     listen_sock = vsf_sysutil_get_ipv6_sock();
66   }
67   vsf_sysutil_activate_reuseaddr(listen_sock);
68 
69   s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
70                                  sizeof(unsigned int), hash_ip);
71   s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
72                                s_ipaddr_size, hash_pid);
73   if (tunable_setproctitle_enable)
74   {
75     vsf_sysutil_setproctitle("LISTENER");
76   }
77   vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
78   vsf_sysutil_install_sighandler(kVSFSysUtilSigHUP, handle_sighup, 0, 1);
79   if (tunable_listen)
80   {
81     struct vsf_sysutil_sockaddr* p_sockaddr = 0;
82     vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
83     vsf_sysutil_sockaddr_set_port(p_sockaddr,
84                                   (unsigned short) tunable_listen_port);
85     if (!tunable_listen_address)
86     {
87       vsf_sysutil_sockaddr_set_any(p_sockaddr);
88     }
89     else
90     {
91       if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
92       {
93         die2("bad listen_address: ", tunable_listen_address);
94       }
95     }
96     retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
97     vsf_sysutil_free(p_sockaddr);
98     if (vsf_sysutil_retval_is_error(retval))
99     {
100       die("could not bind listening IPv4 socket");
101     }
102   }
103   else
104   {
105     struct vsf_sysutil_sockaddr* p_sockaddr = 0;
106     vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
107     vsf_sysutil_sockaddr_set_port(p_sockaddr,
108                                   (unsigned short) tunable_listen_port);
109     if (!tunable_listen_address6)
110     {
111       vsf_sysutil_sockaddr_set_any(p_sockaddr);
112     }
113     else
114     {
115       struct mystr addr_str = INIT_MYSTR;
116       const unsigned char* p_raw_addr;
117       str_alloc_text(&addr_str, tunable_listen_address6);
118       p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
119       str_free(&addr_str);
120       if (!p_raw_addr)
121       {
122         die2("bad listen_address6: ", tunable_listen_address6);
123       }
124       vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
125     }
126     retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
127     vsf_sysutil_free(p_sockaddr);
128     if (vsf_sysutil_retval_is_error(retval))
129     {
130       die("could not bind listening IPv6 socket");
131     }
132   }
133   retval = vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
134   if (vsf_sysutil_retval_is_error(retval))
135   {
136     die("could not listen");
137   }
138   vsf_sysutil_sockaddr_alloc(&p_accept_addr);
139   while (1)
140   {
141     struct vsf_client_launch child_info;
142     void* p_raw_addr;
143     int new_child;
144     int new_client_sock;
145     new_client_sock = vsf_sysutil_accept_timeout(
146         listen_sock, p_accept_addr, 0);
147     if (vsf_sysutil_retval_is_error(new_client_sock))
148     {
149       continue;
150     }
151     ++s_children;
152     child_info.num_children = s_children;
153     child_info.num_this_ip = 0;
154     p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
155     child_info.num_this_ip = handle_ip_count(p_raw_addr);
156     if (tunable_isolate)
157     {
158       if (tunable_http_enable && tunable_isolate_network)
159       {
160         new_child = vsf_sysutil_fork_isolate_all_failok();
161       }
162       else
163       {
164         new_child = vsf_sysutil_fork_isolate_failok();
165       }
166     }
167     else
168     {
169       new_child = vsf_sysutil_fork_failok();
170     }
171     if (new_child != 0)
172     {
173       /* Parent context */
174       vsf_sysutil_close(new_client_sock);
175       if (new_child > 0)
176       {
177         hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
178       }
179       else
180       {
181         /* fork() failed, clear up! */
182         --s_children;
183         drop_ip_count(p_raw_addr);
184       }
185       /* Fall through to while() loop and accept() again */
186     }
187     else
188     {
189       /* Child context */
190       vsf_set_die_if_parent_dies();
191       vsf_sysutil_close(listen_sock);
192       prepare_child(new_client_sock);
193       /* By returning here we "launch" the child process with the same
194        * contract as xinetd would provide.
195        */
196       return child_info;
197     }
198   }
199 }
200 
201 static void
prepare_child(int new_client_sock)202 prepare_child(int new_client_sock)
203 {
204   /* We must satisfy the contract: command socket on fd 0, 1, 2 */
205   vsf_sysutil_dupfd2(new_client_sock, 0);
206   vsf_sysutil_dupfd2(new_client_sock, 1);
207   vsf_sysutil_dupfd2(new_client_sock, 2);
208   if (new_client_sock > 2)
209   {
210     vsf_sysutil_close(new_client_sock);
211   }
212 }
213 
214 static void
drop_ip_count(void * p_raw_addr)215 drop_ip_count(void* p_raw_addr)
216 {
217   unsigned int count;
218   unsigned int* p_count =
219     (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_raw_addr);
220   if (!p_count)
221   {
222     bug("IP address missing from hash");
223   }
224   count = *p_count;
225   if (!count)
226   {
227     bug("zero count for IP address");
228   }
229   count--;
230   *p_count = count;
231   if (!count)
232   {
233     hash_free_entry(s_p_ip_count_hash, p_raw_addr);
234   }
235 }
236 
237 static void
handle_sigchld(void * duff)238 handle_sigchld(void* duff)
239 {
240   unsigned int reap_one = 1;
241   (void) duff;
242   while (reap_one)
243   {
244     reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
245     if (reap_one)
246     {
247       struct vsf_sysutil_ipaddr* p_ip;
248       /* Account total number of instances */
249       --s_children;
250       /* Account per-IP limit */
251       p_ip = (struct vsf_sysutil_ipaddr*)
252         hash_lookup_entry(s_p_pid_ip_hash, (void*)&reap_one);
253       drop_ip_count(p_ip);
254       hash_free_entry(s_p_pid_ip_hash, (void*)&reap_one);
255     }
256   }
257 }
258 
259 static void
handle_sighup(void * duff)260 handle_sighup(void* duff)
261 {
262   (void) duff;
263   /* We don't crash the out the listener if an invalid config was added */
264   tunables_load_defaults();
265   vsf_parseconf_load_file(0, 0);
266 }
267 
268 static unsigned int
hash_ip(unsigned int buckets,void * p_key)269 hash_ip(unsigned int buckets, void* p_key)
270 {
271   const unsigned char* p_raw_ip = (const unsigned char*)p_key;
272   unsigned int val = 0;
273   int shift = 24;
274   unsigned int i;
275   for (i = 0; i < s_ipaddr_size; ++i)
276   {
277     val = val ^ (unsigned int) (p_raw_ip[i] << shift);
278     shift -= 8;
279     if (shift < 0)
280     {
281       shift = 24;
282     }
283   }
284   return val % buckets;
285 }
286 
287 static unsigned int
hash_pid(unsigned int buckets,void * p_key)288 hash_pid(unsigned int buckets, void* p_key)
289 {
290   unsigned int* p_pid = (unsigned int*)p_key;
291   return (*p_pid) % buckets;
292 }
293 
294 static unsigned int
handle_ip_count(void * p_ipaddr)295 handle_ip_count(void* p_ipaddr)
296 {
297   unsigned int* p_count =
298     (unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_ipaddr);
299   unsigned int count;
300   if (!p_count)
301   {
302     count = 1;
303     hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
304   }
305   else
306   {
307     count = *p_count;
308     count++;
309     *p_count = count;
310   }
311   return count;
312 }
313 
314