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