1 /*
2  * Copyright (c) 2002-2006 Tomas Svensson <ts@codepix.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
19  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #ifdef WIN32
32 #define WIN32_LEAN_AND_MEAN	 /* Exclude rarely-used stuff from Windows headers */
33 #include <Winsock2.h>
34 #define snprintf _snprintf
35 #define strcasecmp _stricmp
36 #define strncasecmp _strnicmp
37 typedef   __int32 ssize_t;
38 int write(SOCKET s, void *buf, int len);
39 int read(SOCKET s, void *buf, int len);
40 #else
41 #include <unistd.h>
42 #endif
43 
44 #include "parse.h"
45 #include "misc.h"
46 #include "network.h"
47 #include "tls.h"
48 
49 extern int debug;
50 
51 struct ftp_cmd {
52 	char 	*cmd;
53 	int 	dir;
54 	};
55 
intercept_user_buf(struct user_data * ud,char * buf,ssize_t * len)56 void intercept_user_buf(struct user_data *ud, char *buf, ssize_t *len)
57 {
58 	int	i;
59 
60 	struct ftp_cmd cmd[] = {
61 	 { "LIST", DATA_DOWN },
62 	 { "RETR", DATA_DOWN },
63 	 { "NLST", DATA_DOWN },
64 	 { "STOR", DATA_UP   },
65 	 { "APPE", DATA_UP   },
66 	 { "PORT", DATA_PORT },
67 	 { "EPRT", DATA_PORT },
68 	 { (char*)NULL, 0 }
69 	 };
70 
71 	if (debug)
72 		printf("intercept_user_buf\n");
73 
74 	if (*len < 4)
75 		return;
76 
77 	for (i = 0; cmd[i].cmd; i++)
78 	if (memcmp(buf, cmd[i].cmd, 4) == 0) {
79 		if (cmd[i].dir != DATA_PORT) {
80 			ud->data_direction = cmd[i].dir;
81 			if (debug)
82 				printf("set direction for %s\n", cmd[i].cmd);
83 		} else if (!ud->active) {
84 			unsigned int port;
85 			char ip[17], *ptr;
86 			int r;
87 			struct dns_msg dns;
88 
89 			if (debug)
90 				printf("data port\n");
91 			if ((r = port_to_ipport(buf, ip, sizeof(ip), &port))
92 				== 0) {
93 				ud->active = 1;
94 				if (debug)
95 					printf("PORT - ip: %s port: %u\n",ip, port);
96 				strlcpy(ud->serv_data_host, ip,
97 					sizeof(ud->serv_data_host));
98 				snprintf(ud->serv_data_port, sizeof(ud->serv_data_port),
99 				    "%u", port);
100 				strlcpy(dns.hostname, ip, sizeof(dns.hostname));
101 				strlcpy(dns.port, ud->serv_data_port, sizeof(dns.port));
102 				ptr = (char*)memchr(buf, '\n', *len);
103 
104 				if (ptr != NULL)
105 					*len = 0;
106 				setup_connect_2(ud, &dns, 1);
107 				if (ud->data_connected == CONN_YES)
108 					open_local_dataport(ud);
109 			} else if (r == -1) { // unsupported EPRT network number
110 				*len = 0;
111 				print_to_ud(ud,"522 Network protocol not supported, use (1)\r\n");
112 			}
113 		}
114 	}
115 }
116 
change_serv_buf(struct user_data * ud,char * buf)117 int change_serv_buf(struct user_data *ud, char *buf)
118 {
119 	unsigned int port;
120 	char ip[17];
121 	int r;
122 	struct dns_msg dns;
123 	char *ptr, *ptr2;
124 
125 	if (ud->prot == 'C')
126 		return 0;
127 
128 	if (memcmp(buf,"227 ",4) == 0) { /* PASV reply detected */
129 		ud->epsv = 0;
130 		if ((r = pasv_to_ipport(buf, ip, sizeof(ip), &port))
131 		    == 0) {
132 			if (debug)
133 				printf("ip: %s port: %u\n",ip, port);
134 			strlcpy(ud->serv_data_host, ip,
135 			    sizeof(ud->serv_data_host));
136 			snprintf(ud->serv_data_port, sizeof(ud->serv_data_port),
137 			    "%u", port);
138 			strlcpy(dns.hostname, ip, sizeof(dns.hostname));
139 			strlcpy(dns.port, ud->serv_data_port, sizeof(dns.port));
140 			setup_connect_2(ud, &dns, 1);
141 			if (ud->data_connected == CONN_YES) {
142 				open_local_dataport(ud);
143 			}
144 			return 1;
145 		}
146 		else printf("change_serv_buf failed %d (%s)\n", r, buf);
147 	} else if (memcmp(buf,"229 ",4) == 0) { /* EPSV reply detected */
148 		if (debug)
149 			printf("EPSV (%s)\n", buf);
150 		ud->epsv = 1;
151 		ptr = strstr(buf, "(|||");
152 		if (ptr == NULL)
153 			return 0;
154 		ptr += 4;
155 		if (strlen(ptr) < 3) return 0;
156 		ptr2 = strstr(ptr, "|)");
157 		if (ptr == NULL)
158 			return 0;
159 		*ptr2 = '\0';
160 		strlcpy(ud->serv_data_port, ptr, sizeof(ud->serv_data_port));
161 		strlcpy(ud->serv_data_host, ud->serv_host,
162 		    sizeof(ud->serv_data_host));
163 		/*
164 		strlcpy(dns.hostname, ud->serv_host, sizeof(dns.hostname));
165 		*/
166 		dns = ud->serv_dns;
167 		strlcpy(dns.port, ud->serv_data_port, sizeof(dns.port));
168 		setup_connect_2(ud, &dns, 1);
169 		if (ud->data_connected == CONN_YES)
170 			open_local_dataport(ud);
171 		return 1;
172 
173 	}
174 	return 0;
175 }
176 
open_local_dataport(struct user_data * ud)177 void open_local_dataport(struct user_data *ud)
178 {
179 	char 	port[6];
180 	char 	pasv[25];
181 	char	tmp[80], *ep;
182 	char 	myip[NI_MAXHOST];
183 
184 	port[0] = '\0';
185 	if (ud->active)
186 		get_local_ip(ud->serv_fd, myip, sizeof(myip));
187 	else
188 		get_local_ip(ud->user_fd, myip, sizeof(myip));
189 	if (debug)
190 		printf("my local ip is %s\n", myip);
191 	ud->user_data_fd = setup_listen(5, myip, port, sizeof(port));
192 	if (debug)
193 		printf("open_local_dataport: fd = %d, port = %s\n",ud->user_data_fd, port);
194 	ipport_to_pasv(pasv, sizeof(pasv), myip, strtol(port, &ep, 10));
195 	if (ud->active) {
196 		snprintf(tmp, sizeof(tmp), "PORT %s\r\n", pasv);
197 		print_to_serv(ud, tmp);
198 	} else {
199 		if (ud->epsv)
200 			snprintf(tmp, sizeof(tmp), "229 Entering Extended Passive Mode (|||%u|)\r\n",(unsigned int)strtol(port, &ep, 10));
201 		else
202 			snprintf(tmp, sizeof(tmp), "227 Entering Passive Mode (%s)\r\n",pasv);
203 		write(ud->user_fd, tmp, strlen(tmp));
204 	}
205 	if (debug)
206 		printf("sent %s",tmp);
207 	ud->data_connected = CONN_DATA_LISTEN;
208 }
209 
pasv_to_ipport(char * buf,char * ip,int iplen,unsigned int * port)210 int pasv_to_ipport(char *buf, char *ip, int iplen, unsigned int *port)
211 {
212 	char 	*ep, *ptr, *ptr2;
213 	int	i;
214 	int 	num;
215 
216 	if ( (ptr = strchr(buf, '(')) == NULL)
217 		return 1;
218 
219 	ptr2 = ++ptr;
220 	for (i = 0; i<4; i++) {
221 		if ((ptr = strchr(++ptr, ',') ) == NULL)
222 			return 2;
223 		*ptr = '.';
224 	}
225 	*ptr++ = '\0';
226 	strlcpy(ip, ptr2, iplen);
227 	ptr2 = ptr;
228 	if ((ptr = strchr(ptr, ',')) == NULL)
229 		return 3;
230 	*ptr++ = '\0';
231 	num = strtol(ptr2, &ep, 10);
232 	if (num < 0 || *ep != '\0')
233 		return 4;
234 	*port = num * 256;
235 	ptr2 = ptr;
236 	if ((ptr = strchr(ptr, ')')) == NULL)
237 		return 5;
238 	*ptr = '\0';
239 	num = strtol(ptr2, &ep, 10);
240 	if (num < 0 || *ep != '\0') {
241 		printf("pasv_to_ipport FAILED with %s that got changed into %d (at %s)\n", ptr2, num, ep);
242 		return 6;
243 	}
244 	*port += num;
245 
246 	return 0;
247 }
248 
249 void
ipport_to_pasv(char * buf,int len,const char * ip,unsigned int port)250 ipport_to_pasv(char *buf, int len, const char *ip, unsigned int port)
251 {
252 	char 	*ptr;
253 	char	tmp[16];
254 
255 	strlcpy(tmp, ip, sizeof(tmp));
256 	while ( (ptr = strchr(tmp, '.')) )
257 		*ptr = ',';
258 	snprintf(buf, len, "%s,%d,%d", tmp, port / 256, port % 256);
259 }
260 
port_to_ipport(char * buf,char * ip,int iplen,unsigned int * port)261 int port_to_ipport(char *buf, char *ip, int iplen, unsigned int *port)
262 {
263 	char 	*ep, *ptr, *ptr2, sep;
264 	int	i;
265 	int 	num;
266 
267 	if (memcmp(buf, "PORT", 4) == 0) {
268 
269 		if ( (ptr = strchr(buf, ' ')) == NULL)
270 			return 1;
271 
272 		ptr2 = ++ptr;
273 		for (i = 0; i<4; i++) {
274 			if ((ptr = strchr(++ptr, ',') ) == NULL)
275 				return 2;
276 			*ptr = '.';
277 		}
278 		*ptr++ = '\0';
279 		strlcpy(ip, ptr2, iplen);
280 		ptr2 = ptr;
281 		if ((ptr = strchr(ptr, ',')) == NULL)
282 			return 3;
283 		*ptr++ = '\0';
284 		num = strtol(ptr2, &ep, 10);
285 		if (num < 0 || *ep != '\0')
286 			return 4;
287 		*port = num * 256;
288 		ptr2 = ptr;
289 		if ((ptr = strchr(ptr, '\r')) == NULL)
290 			return 5;
291 		*ptr = '\0';
292 		num = strtol(ptr2, &ep, 10);
293 		if (num < 0 || *ep != '\0') {
294 			printf("port_to_ipport FAILED with %s that got changed into %d (at %s)\n", ptr2, num, ep);
295 			return 6;
296 		}
297 		*port += num;
298 	} else { // EPRT
299 		if ((ptr = strchr(buf, ' ')) == NULL)
300 			return 1;
301 		sep = *(++ptr);
302 		if ((ptr2 = strchr(++ptr, sep)) == NULL)
303 			return 1;
304 		*ptr2 = '\0';
305 		num = strtol(ptr, &ep, 10);
306 		if (num != 1)
307 			return -1; // unsupported network
308 		ptr2++; // ptr2 now points to beginning of the ip address
309 		if ((ptr = strchr(ptr2, sep)) == NULL)
310 			return 1;
311 		*ptr = '\0';
312 		strlcpy(ip, ptr2, iplen);
313 		ptr++; // ptr now points to beginning of the port number
314 		if ((ptr2 = strchr(ptr, sep)) == NULL)
315 			return 1;
316 		*ptr2 = '\0';
317 		num = strtol(ptr, &ep, 10);
318 		if (num < 0 || *ep != '\0') {
319 			printf("port_to_ipport FAILED with %s that got changed into %d (at %s)\n", ptr2, num, ep);
320 			return 6;
321 		}
322 		*port = num;
323 
324 	}
325 	return 0;
326 }
327 
328 int
parse_serv_buf(struct user_data * ud,int index,char * ucertspath,char * cafile)329 parse_serv_buf(struct user_data *ud, int index, char *ucertspath, char *cafile)
330 {
331 	int size;
332 	char dst[BUF_SIZE], s[100];
333 
334 	if ( (size = extr_str(ud->serv_input, BUF_SIZE, dst, sizeof(dst))) == 0)
335 		return 1; /* Nothing could be extracted */
336 
337 
338 	if ((ud->serv_status == SERV_CONN) && (strncasecmp(dst,"220 ",4) == 0) ) {
339 		print_to_serv(ud, "AUTH TLS\r\n");
340 		ud->serv_status = SERV_AUTH;
341 	} else if ((ud->serv_status == SERV_AUTH) && (strncasecmp(dst,"234 ",4) == 0) ) {
342 		ud->serv_status = SERV_TLS;
343 		tls_auth(ud, 0, ucertspath, cafile);
344 	} else if ((ud->serv_status == SERV_TLS_OK) && (strncasecmp(dst,"200 ",4) == 0) ) {
345 		ud->serv_status = SERV_PBSZ;
346 		snprintf(s, sizeof(s), "PROT %c\r\n", ud->prot);
347 		if (debug)
348 			printf(s);
349 		print_to_serv(ud,s);
350 	} else if ((ud->serv_status == SERV_PBSZ) && (strncasecmp(dst,"200 ",4) == 0) ) {
351 		ud->serv_status = SERV_PROT;
352 		snprintf(s, sizeof(s), "USER %s\r\n",ud->user);
353 		print_to_serv(ud, s);
354 		ud->delay_prot = 0;
355 	} else if ((ud->serv_status == SERV_PBSZ) && (strncasecmp(dst,"530 ",4) == 0) ) {
356 		ud->serv_status = SERV_PROT;
357 		snprintf(s, sizeof(s), "USER %s\r\n",ud->user);
358 		print_to_serv(ud, s);
359 		ud->delay_prot = 1;
360 	} else if ((ud->serv_status == SERV_PROT) && (strncasecmp(dst,"331 ",4) == 0) ) {
361 		snprintf(s, sizeof(s), "PASS %s\r\n",ud->pass);
362 		print_to_serv(ud, s);
363 		if (!ud->delay_prot)
364 			ud->serv_status = SERV_FLOW;
365 	} else if (ud->delay_prot && (ud->serv_status == SERV_PROT) && (strncasecmp(dst,"230 ",4) == 0) ) {
366 		snprintf(s, sizeof(s), "PROT %c\r\n", ud->prot);
367 		if (debug)
368 			printf(s);
369 		print_to_serv(ud,s);
370 	} else if (ud->delay_prot && (ud->serv_status == SERV_PROT) && (strncasecmp(dst,"200 ",4) == 0) ) {
371 		write(ud->user_fd, "230 Bypassed login text because the ftpd can't handle PROT before USER.\r\n", 73);
372 		ud->serv_status = SERV_FLOW;
373 	}
374 	memmove(ud->serv_input, &ud->serv_input[size], BUF_SIZE - size - 1);
375 	ud->serv_ptr -= size;
376 	return 0;
377 }
378 
379 
380 int
parse_buf(struct user_data * ud,int index,int dns_write_pipe,char * token)381 parse_buf(struct user_data *ud, int index, int dns_write_pipe, char *token)
382 {
383 	int size, updated;
384 	char dst[BUF_SIZE], *ptr, s[100], tmp[200];
385 
386 	if ( (size = extr_str(ud->user_input, BUF_SIZE, dst, sizeof(dst))) == 0)
387 		return 1; /* Nothing could be extracted */
388 
389 	/* Parse starts here and is ugly */
390 
391 	if ((ud->connected == CONN_NO) && (strncasecmp(dst,"USER ",5) == 0)) {
392 		strlcpy(tmp, dst + 5, sizeof(tmp));
393 		ptr = memchr(tmp, token[1], strlen(tmp));
394 		if (ptr != NULL) {
395 			*ptr++ = 0;
396 		    if ( (strlen(tmp) > 0) && (strlen(ptr) > 0) ) {
397 				updated = 0;
398 				if (tmp[0] == token[0]) {
399 					ud->prot = 'C'; /* Encrypt control only */
400 					strlcpy(ud->user, tmp + 1, sizeof(ud->user));
401 					memmove(tmp, tmp + 1, strlen(tmp));
402 					updated = 1;
403 				}
404 				if (tmp[0] == token[3]) { // Implicit SSL crap
405 					if (!updated)
406 						ud->prot = 'P'; /* Encrypt everything */
407 					strlcpy(ud->user, tmp + 1, sizeof(ud->user));
408 					memmove(tmp, tmp + 1, strlen(tmp));
409 					ud->issl = 1;
410 					updated = 1;
411 				}
412 				if (tmp[0] == token[4]) { // + Set security level
413 					if (!updated)
414 						ud->prot = 'P'; /* Encrypt everything */
415 					if ((tmp[1] - '0' >= 0) && (tmp[1] - '0' <= 4))
416 						ud->sec_level = tmp[1] - '0';
417 					strlcpy(ud->user, tmp + 2, sizeof(ud->user));
418 					updated = 1;
419 				}
420 				if (!updated) {
421 					ud->prot = 'P'; /* Encrypt everything */
422 					strlcpy(ud->user, tmp, sizeof(ud->user));
423 				}
424 				strlcpy(ud->serv_host, ptr, sizeof(ud->serv_host));
425 				ptr = memchr(ud->serv_host, token[2], strlen(ud->serv_host));
426 				if (ptr != NULL) {
427 					*ptr++ = 0;
428 					if (strlen(ptr) > 0)
429 						strlcpy(ud->serv_port, ptr, sizeof(ud->serv_port));
430 					else
431 						ptr = NULL;
432 				}
433 
434 				if (ptr == NULL)
435 					strlcpy(ud->serv_port, "21", sizeof(ud->serv_port));
436 				ud->connected = CONN_USER;
437 				if (debug)
438 					printf("Username: %s, Host: %s, Port: %s\n",ud->user,
439 					    ud->serv_host, ud->serv_port);
440 				snprintf(s, sizeof(s), "331 Password required for %s.\r\n", ud->user);
441 				print_to_ud(ud, s);
442 			}
443 		} else {
444 			if (debug)
445 				printf("don't find any @\n");
446 			user_close(ud);
447 		}
448 	} else
449 
450 	/* Attempt connection directly after receiving PASS */
451 
452 	if ((ud->connected == CONN_USER) && (strncasecmp(dst,"PASS ",5) == 0)) {
453 		strlcpy(ud->pass, dst + 5, sizeof(ud->pass));
454 		ud->connected = CONN_PASS;
455 		setup_connect_1(ud, index, ud->serv_host, ud->serv_port, dns_write_pipe);
456 	} else
457 
458 	/* Reject AUTH stuff */
459 
460 	if ((ud->connected == CONN_NO) && (strncasecmp(dst,"AUTH ",5) == 0)) {
461 		snprintf(s, sizeof(s), "502 RFC 2228 authentication not implemented.\r\n");
462 		print_to_ud(ud, s);
463 	}
464 
465 	memmove(ud->user_input, &ud->user_input[size], BUF_SIZE - size - 1);
466 	ud->user_ptr -= size;
467 	return 0;
468 }
469 
470