1 
2 /*
3 
4     File: ftpproxy/ftp.c
5 
6     Copyright (C) 1999, 2000  Wolfgang Zekoll  <wzk@quietsche-entchen.de>
7     Copyright (C) 2000, 2003  Andreas Schoenberg  <asg@ftpproxy.org>
8 
9     This software is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 
23  */
24 
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31 
32 #include <time.h>
33 #include <signal.h>
34 #include <sys/wait.h>
35 #include <ctype.h>
36 #include <errno.h>
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/fcntl.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #include <netinet/in.h>
44 #include <netinet/tcp.h>
45 #include <arpa/inet.h>
46 #include <syslog.h>
47 #include <sys/time.h>
48 
49 #include "ftp.h"
50 #include "ip-lib.h"
51 #include "lib.h"
52 
53 
54 typedef struct _ftpcmd {
55     char	name[20];
56     int		par, ispath, useccp;
57     int		resp;
58     int		log;
59     } ftpcmd_t;
60 
61 ftpcmd_t cmdtab[] = {
62 
63 	/*
64 	 * Einfache FTP Kommandos.
65 	 */
66 
67     { "ABOR", 0, 0, 0,	225, 1 },		/* oder 226 */
68     { "ACCT", 1, 0, 0,	230, 0 },
69     { "CDUP", 1, 1, 1,	200, 1 },
70     { "CWD",  1, 1, 1,	250, 1 },
71     { "DELE", 1, 1, 1,	250, 1 },
72     { "NOOP", 0, 0, 0,	200, 0 },
73     { "MDTM", 1, 1, 1,	257, 1 },
74     { "MKD",  1, 1, 1,	257, 1 },
75     { "MODE", 1, 0, 0,	200, 0 },
76     { "PWD",  0, 0, 0,	257, 0 },
77     { "QUIT", 0, 0, 0,	221, 0 },
78     { "REIN", 0, 0, 0,	0, /* 220, */ 0 },	/* wird nicht unterstuetzt */
79     { "REST", 1, 0, 0,	350, 0 },
80     { "RNFR", 1, 1, 1,	350, 1 },
81     { "RNTO", 1, 1, 1,	250, 1 },
82     { "RMD",  1, 1, 1,	250, 1 },
83     { "SITE", 1, 0, 1,	200, 0 },
84     { "SIZE", 1, 1, 1,	213, 1 },
85     { "SMNT", 1, 0, 0,	250, 0 },
86     { "STAT", 1, 1, 1,	211, 0 },			/* oder 212, 213 */
87     { "STRU", 1, 0, 0,	0, /* 200, */ 0 },	/* wird nicht unterstuetzt */
88     { "SYST", 0, 0, 0,	215, 0 },
89     { "TYPE", 1, 0, 0,	200, 0 },
90     { "XCUP", 1, 1, 1,	200, 1 },
91     { "XCWD", 1, 1, 1,	250, 1 },
92     { "XMKD", 1, 1, 1,	257, 1 },
93     { "XPWD", 0, 0, 0,	257, 0 },
94     { "XRMD", 1, 1, 1,	250, 1 },
95 
96 	/*
97 	 * Nur der Vollstaendigkeit halber: FTP Kommandos die gesondert
98 	 * behandelt werden.
99 	 */
100 
101     { "LIST", 1, 1, 1,	0, 0 },
102     { "NLST", 1, 1, 1,	0, 0 },
103     { "PORT", 1, 0, 0,	0, /* 200, */ 0 },
104     { "PASV", 0, 0, 0,	0, /* 200, */ 0 },
105     { "ALLO", 1, 0, 0,	0, /* 200, */ 0 },
106     { "RETR", 1, 1, 1,	0, 0 },
107     { "STOR", 1, 1, 1,	0, 0 },
108     { "STOU", 0, 0, 1,	0, 0 },
109     { "APPE", 1, 1, 1,	0, 0 },
110     { "HELP", 0, 0, 0,	0, 0 },
111     { "FEAT", 0, 0, 0,	0, 0 },
112     { "",     0, 0, 0,	0, 0 }
113     };
114 
115 
get_interface_info(int pfd,char * ip,int max)116 unsigned get_interface_info(int pfd, char *ip, int max)
117 {
118 	int	size;
119 	unsigned int port;
120 	struct sockaddr_in saddr;
121 
122 	size = sizeof(saddr);
123 	if (getsockname(pfd, (struct sockaddr *) &saddr, &size) < 0) {
124 		syslog(LOG_NOTICE, "-ERR: can't get interface info: %s", strerror(errno));
125 		exit (-1);
126 		}
127 
128 	copy_string(ip, (char *) inet_ntoa(saddr.sin_addr), max);
129 	port = ntohs(saddr.sin_port);
130 
131 	return (port);
132 }
133 
get_client_info(ftp_t * x,int pfd)134 int get_client_info(ftp_t *x, int pfd)
135 {
136 	int	size;
137 	struct sockaddr_in saddr;
138 	struct in_addr *addr;
139 	struct hostent *hostp = NULL;
140 
141 	*x->client = 0;
142 	size = sizeof(saddr);
143 	if (getpeername(pfd, (struct sockaddr *) &saddr, &size) < 0 )
144 		return (-1);
145 
146 	copy_string(x->client_ip, (char *) inet_ntoa(saddr.sin_addr), sizeof(x->client_ip));
147 
148 	if (x->config->numeric_only == 1)
149 		copy_string(x->client, x->client_ip, sizeof(x->client));
150 	else {
151 		addr = &saddr.sin_addr,
152 		hostp = gethostbyaddr((char *) addr,
153 				sizeof (saddr.sin_addr.s_addr), AF_INET);
154 
155 		copy_string(x->client, hostp == NULL? x->client_ip: hostp->h_name, sizeof(x->client));
156 		}
157 
158 	strlwr(x->client);
159 
160 	return (0);
161 }
162 
163 
164 	/*
165 	 * Basic I/O functions
166 	 */
167 
close_ch(ftp_t * x,dtc_t * ch)168 int close_ch(ftp_t *x, dtc_t *ch)
169 {
170 	if (ch->isock >= 0)
171 		close(ch->isock);
172 
173 	if (ch->osock >= 0)
174 		close (ch->osock);
175 
176 	ch->isock     = -1;
177 	ch->osock     = -1;
178 	ch->state     = 0;
179 	ch->operation = 0;
180 	ch->seen150   = 0;
181 
182 	return (0);
183 }
184 
getc_fd(ftp_t * x,int fd)185 int getc_fd(ftp_t *x, int fd)
186 {
187 	int	c;
188 	bio_t	*bio;
189 
190 	if (fd == 0)
191 		bio = &x->cbuf;
192 	else if (fd == x->fd.server)
193 		bio = &x->sbuf;
194 	else {
195 		syslog(LOG_NOTICE, "-ERR: internal bio/fd error");
196 		exit (1);
197 		}
198 
199 	if (bio->here >= bio->len) {
200 		int	rc, max, bytes, earlyreported;
201 		struct timeval tov;
202 		fd_set	available, fdset;
203 
204 		bio->len = bio->here = 0;
205 		earlyreported = 0;
206 
207 		FD_ZERO(&fdset);
208 		FD_SET(fd, &fdset);
209 /*		x->fd.max = fd; */
210 		max = fd;
211 
212 		if (x->ch.operation == 0)
213 			/* nichts */ ;
214 		else if (x->ch.state == PORT_LISTEN) {
215 			if (x->ch.mode == MODE_PORT) {
216 				FD_SET(x->ch.osock, &fdset);
217 				if (x->ch.osock > max)
218 					max = x->ch.osock;
219 
220 				x->ch.active = x->ch.osock;
221 				}
222 			else if (x->ch.mode == MODE_PASSIVE) {
223 				FD_SET(x->ch.isock, &fdset);
224 				if (x->ch.isock > max)
225 					max = x->ch.isock;
226 
227 				x->ch.active = x->ch.isock;
228 				}
229 			else {
230 				syslog(LOG_NOTICE, "-ERR: internal mode error");
231 				exit (-1);
232 				}
233 			}
234 		else if (x->ch.state == PORT_CONNECTED  &&  x->ch.seen150 == 1) {
235 			FD_SET(x->ch.active, &fdset);
236 			if (x->ch.active > max)
237 				max = x->ch.active;
238 			}
239 
240 		bytes = 0;
241 		while (1) {
242 /*			memmove(&available, &fdset, sizeof(fd_set)); */
243 			available = fdset;
244 			tov.tv_sec  = x->config->timeout;
245 			tov.tv_usec = 0;
246 
247 			if (debug >= 2)
248 				fprintf (stderr, "select max= %d\n", max);
249 
250 			rc = select(max + 1, &available, (fd_set *) NULL, (fd_set *) NULL, &tov);
251 			if (rc < 0) {
252 				syslog(LOG_NOTICE, "select() error: %s\n", strerror(errno));
253 				break;
254 				}
255 			else if (rc == 0) {
256 				syslog(LOG_NOTICE, "connection timed out: client= %s, server= %s:%u",
257 					x->client, x->server.name, x->server.port);
258 				return (-1);
259 				}
260 
261 			if (FD_ISSET(fd, &available)) {
262 				if ((bytes = read(fd, bio->buffer, sizeof(bio->buffer) - 2)) <= 0) {
263 					if (debug != 0) {
264 						if (bytes == 0)
265 							fprintf (stderr, "received zero bytes on fd %d\n", fd);
266 						else
267 							fprintf (stderr, "received %d bytes on fd %d, errno= %d, error= %s\n", bytes, fd, errno, strerror(errno));
268 						}
269 
270 					return (-1);
271 					}
272 
273 				break;
274 				}
275 			else if (FD_ISSET(x->ch.active, &available)) {
276 				if (x->ch.state == PORT_LISTEN) {
277 					int	sock, adrlen;
278 					struct sockaddr_in adr;
279 
280 					earlyreported = 0;
281 					adrlen = sizeof(struct sockaddr);
282 					sock = accept(x->ch.active, (struct sockaddr *) &adr, &adrlen);
283 					if (debug != 0)
284 						fprintf (stderr, "accept() on socket\n");
285 
286 					if (sock < 0) {
287 						syslog(LOG_NOTICE, "-ERR: accept error: %s", strerror(errno));
288 						exit (1);
289 						}
290 					else {
291 						char	remote[80];
292 
293 						copy_string(remote, inet_ntoa(adr.sin_addr), sizeof(remote));
294 						if (debug)
295 							fprintf (stderr, "connection from %s\n", remote);
296 
297 						/*
298 						 * Gegenstelle ueberpruefen.
299 						 */
300 
301 						if (x->ch.mode == MODE_PORT) {
302 							if (strcmp(x->server.ipnum, remote) != 0) {
303 								if (x->config->allow_anyremote != 0)
304 									/* configuration tells us not to care -- 31JAN02asg */ ;
305 								else {
306 									syslog(LOG_NOTICE, "-ERR: unexpected connect: %s, expected= %s", remote, x->server.ipnum);
307 									exit (1);
308 									}
309 								}
310 							}
311 						else {
312 							if (strcmp(x->client_ip, remote) != 0) {
313 								if (x->config->allow_anyremote != 0)
314 									/* ok -- 31JAN02asg */ ;
315 								else {
316 									syslog(LOG_NOTICE, "-ERR: unexpected connect: %s, expected= %s", remote, x->client_ip);
317 									exit (1);
318 									}
319 								}
320 							}
321 						}
322 
323 					/*
324 					 * Datenkanal zur anderen Seite aufbauen.
325 					 */
326 
327 					if (x->ch.mode == MODE_PORT) {
328 						dup2(sock, x->ch.osock);
329 						close (sock);
330 						x->ch.state = PORT_CONNECTED;
331 						if (debug)
332 							fprintf (stderr, "osock= %d\n", x->ch.osock);
333 
334 						if ((x->ch.isock = openip(x->ch.client.ipnum, x->ch.client.port, x->interface, x->config->dataport)) < 0) {
335 							syslog(LOG_NOTICE, "-ERR: can't connect to client: %s", strerror(errno));
336 							exit (1);
337 							}
338 
339 						if (debug)
340 							fprintf (stderr, "isock= %d\n", x->ch.isock);
341 						}
342 					else if (x->ch.mode == MODE_PASSIVE) {
343 						dup2(sock, x->ch.isock);
344 						close (sock);
345 						x->ch.state = PORT_CONNECTED;
346 						if (debug)
347 							fprintf (stderr, "isock= %d\n", x->ch.isock);
348 
349 						if ((x->ch.osock = openip(x->ch.server.ipnum, x->ch.server.port, x->config->sourceip, 0)) < 0) {
350 							syslog(LOG_NOTICE, "-ERR: can't connect to server: %s", strerror(errno));
351 							exit (1);
352 							}
353 
354 						if (debug)
355 							fprintf (stderr, "osock= %d\n", x->ch.osock);
356 						}
357 
358 
359 					/*
360 					 * Setzen der Datenquelle (Server oder Client).
361 					 */
362 
363 					if (x->ch.operation == OP_GET) {
364 						x->ch.active = x->ch.osock;
365 						x->ch.other  = x->ch.isock;
366 						}
367 					else if (x->ch.operation == OP_PUT) {
368 						x->ch.active = x->ch.isock;
369 						x->ch.other  = x->ch.osock;
370 						}
371 					else {
372 						syslog(LOG_NOTICE, "-ERR: transfer operation error");
373 						exit (1);
374 						}
375 
376 					if (x->ch.seen150 == 0) {
377 
378 						/*
379 						 * And finally ... another attempt to solve the short
380 						 * data transmission timing problem: If we didn't receive
381 						 * the 150 response yet from the server we deactivate the
382 						 * data channel until we have the 150 -- 030406asg
383 						 */
384 
385 						if (debug >= 2)
386 							fprintf (stderr, "150 not seen, deactivating data channel\n");
387 
388 						FD_ZERO(&fdset);
389 						FD_SET(fd, &fdset);
390 						max = fd;
391 						}
392 					else {
393 						if (debug >= 2)
394 							fprintf (stderr, "150 already seen, activating data channel\n");
395 
396 						FD_ZERO(&fdset);
397 						FD_SET(fd, &fdset);
398 						FD_SET(x->ch.active, &fdset);
399 						max = (fd > x->ch.active)? fd: x->ch.active;
400 						}
401 
402 					if (debug)
403 						fprintf (stderr, "active= %d, other= %d\n", x->ch.active, x->ch.other);
404 
405 					x->ch.bytes = 0;
406 					x->ch.started = time(NULL);
407 					}
408 				else if (x->ch.state == PORT_CONNECTED) {
409 					int	wrote;
410 					char	buffer[FTPMAXBSIZE + 10];
411 
412 					if (x->ch.operation == 0) {
413 						if (earlyreported == 0) {
414 							earlyreported = 1;
415 							syslog(LOG_NOTICE, "early write/read event, sleeping 2 seconds");
416 							sleep(2);
417 							continue;
418 							}
419 						}
420 
421 					bytes = read(x->ch.active, buffer, x->config->bsize /* sizeof(buffer) */ );
422 
423 					/*
424 					 * Handling servers that close the data connection -- 24APR02asg
425 					 */
426 
427 					wrote = 0;
428 					if ((bytes > 0)  &&  ((wrote = write(x->ch.other, buffer, bytes)) == bytes))
429 						x->ch.bytes += bytes;
430 					else {
431 						if (wrote < 0)
432 							syslog(LOG_NOTICE, "error writing data channel, error= %s", strerror(errno));
433 
434 						if (debug)
435 							fprintf (stderr, "closing data connection\n");
436 
437 						close_ch(x, &x->ch);
438 						FD_ZERO(&fdset);
439 						FD_SET(fd, &fdset);
440 						max = fd;
441 
442 						return (1);
443 						}
444 					}
445 				}
446 			}
447 
448 		bio->len  = bytes;
449 		bio->here = 0;
450 		}
451 
452 	if (bio->here >= bio->len)
453 		return (-1);
454 
455 	c = (unsigned char) bio->buffer[bio->here++];
456 	return (c);
457 }
458 
readline_fd(ftp_t * x,int fd,char * line,int size)459 char *readline_fd(ftp_t *x, int fd, char *line, int size)
460 {
461 	int	c, k;
462 
463 	*line = 0;
464 	size = size - 2;
465 
466 	c = getc_fd(x, fd);
467 	if (c < 0)
468 		return (NULL);
469 	else if (c == 1) {
470 		strcpy(line, "\001");
471 		return (line);
472 		}
473 
474 	k = 0;
475 	while (c > 0  &&  c != '\n'  &&  c != 0) {
476 		if (k < size)
477 			line[k++] = c;
478 
479 		c = getc_fd(x, fd);
480 		}
481 
482 	line[k] = 0;
483 	noctrl(line);
484 
485 	k = 0;
486 	while ((c = (unsigned char ) line[k]) != 0  &&  c > 126)
487 		k++;
488 
489 	if (k > 0)
490 		copy_string(line, &line[k], size);
491 
492 	return (line);
493 }
494 
495 
cfgets(ftp_t * x,char * line,int size)496 char *cfgets(ftp_t *x, char *line, int size)
497 {
498 	char	*p;
499 
500 	*line = 0;
501 	if ((p = readline_fd(x, 0, line, size)) == NULL)
502 		return (NULL);
503 	else if (debug != 0)
504 		fprintf (stderr, "CLI >>>: %s\n", p);
505 
506 	return (line);
507 }
508 
cfputs(ftp_t * x,char * line)509 int cfputs(ftp_t *x, char *line)
510 {
511 	char	buffer[310];
512 
513 	if (debug)
514 		fprintf (stderr, ">>> CLI: %s\n", line);
515 
516 	snprintf (buffer, sizeof(buffer) - 2, "%s\r\n", line);
517 	write(1, buffer, strlen(buffer));
518 
519 	return (0);
520 }
521 
522 
sfgets(ftp_t * x,char * line,int size)523 char *sfgets(ftp_t *x, char *line, int size)
524 {
525 	char *p;
526 
527 	*line = 0;
528 	if ((p = readline_fd(x, x->fd.server, line, size)) == NULL)
529 		return (NULL);
530 	else if (debug != 0)
531 		fprintf (stderr, "SVR >>>: %s\n", p);
532 
533 	return (line);
534 }
535 
sfputs(ftp_t * x,char * format,...)536 int sfputs(ftp_t *x, char *format, ...)
537 {
538 	int	len;
539 	char	buffer[310];
540 	va_list	ap;
541 
542 	va_start(ap, format);
543 	vsnprintf (buffer, sizeof(buffer) - 10, format, ap);
544 	va_end(ap);
545 
546 	if (debug)
547 		fprintf (stderr, ">>> SVR: %s\n", buffer);
548 
549 	/*
550 	 * There are firewalls that don't like command to be split in
551 	 * two packets.  Notice: the `- 10' above is really important
552 	 * to protect the proxy against buffer overflows.
553 	 */
554 
555 	strcat(buffer, "\r\n");
556 	len = strlen(buffer);
557 
558 	/*
559 	 * SIGPIPE is catched but then ignored, we have to handle it
560 	 * one our own now -- 24APR02asg
561 	 */
562 
563 	if (write(x->fd.server, buffer, len) != len) {
564 		syslog(LOG_NOTICE, "-ERR: error writing control connect, error= %s", strerror(errno));
565 		exit (1);
566 		}
567 
568 /*
569  *	write(x->fd.server, buffer, strlen(buffer));
570  *	write(x->fd.server, "\r\n", 2);
571  */
572 	return (0);
573 }
574 
sfputc(ftp_t * x,char * command,char * parameter,char * line,int size,char ** here)575 int sfputc(ftp_t *x, char *command, char *parameter, char *line, int size, char **here)
576 {
577 	int	rc;
578 	char	*p, buffer[300];
579 
580 	if (command != NULL  &&  *command != 0) {
581 		if (parameter != NULL  &&  *parameter != 0)
582 			snprintf (buffer, sizeof(buffer) - 2, "%s %s", command, skip_ws(parameter));
583 		else
584 			copy_string(buffer, command, sizeof(buffer));
585 
586 		sfputs(x, "%s", buffer);
587 		}
588 
589 	if (sfgets(x, line, size) == NULL) {
590 		if (debug != 0)
591 			fprintf (stderr, "server disappered in sfputc(), pos #1\n");
592 
593 		return (-1);
594 		}
595 	else if (strlen(line) < 3) {
596 		if (debug != 0)
597 			fprintf (stderr, "short server reply in sfputc()\n");
598 
599 		return (-1);
600 		}
601 
602 	rc = atoi(line);
603 	if (line[3] != ' '  &&  line[3] != 0) {
604         	while (1) {
605                 	if (sfgets(x, line, size) == NULL) {
606 				syslog(LOG_NOTICE, "-ERR: lost server while reading client greeting: %s", x->server.name);
607 				exit (1);
608 				}
609 
610 			if (strlen(line) < 3)
611 				/* line too short to be response's last line */ ;
612 			else if (line[3] != ' '  &&  line[3] != 0)
613 				/* neither white space nor EOL at position #4 */ ;
614 			else if (line[0] >= '0'  &&  line[0] <= '9'  &&  atoi(line) == rc)
615                        		break;		/* status code followed by EOL or blank detected */
616 			}
617         	}
618 
619 	if (here != NULL) {
620 		p = skip_ws(&line[3]);
621 		*here = p;
622 		}
623 
624 	return (rc);
625 }
626 
627 
628 
doquit(ftp_t * x)629 int doquit(ftp_t *x)
630 {
631 	int	rc;
632 	char	resp[200];
633 
634 	if ((rc = sfputc(x, "QUIT", "", resp, sizeof(resp), NULL)) != 221)
635 		syslog(LOG_NOTICE, "unexpected resonse to QUIT: %s", resp);
636 
637 	cfputs(x, "221 goodbye");
638 	syslog(LOG_NOTICE, "%d QUIT", rc);
639 
640 	return (0);
641 }
642 
643 
_getipnum(char * line,char ** here,char * ip,int size)644 char *_getipnum(char *line, char **here, char *ip, int size)
645 {
646 	int	c, i, k;
647 
648 	copy_string(ip, line, size);
649 	k = 0;
650 	for (i=0; (c = ip[i]) != 0; i++) {
651 		if (c == ',') {
652 			if (k < 3) {
653 				ip[i] = '.';
654 				k++;
655 				}
656 			else {
657 				ip[i++] = 0;
658 				break;
659 				}
660 			}
661 		}
662 
663 	if (here != NULL)
664 		*here = &line[i];
665 
666 	return (ip);
667 }
668 
_getport(char * line,char ** here)669 unsigned long _getport(char *line, char **here)
670 {
671 	unsigned long port;
672 	char	*p;
673 
674 	p = line;
675 	port = strtoul(p, &p, 10);
676 	if (*p != ',')
677 		return (0);
678 
679 	p++;
680 	port = (port << 8) + strtoul(p, &p, 10);
681 	if (here != NULL)
682 		*here = p;
683 
684 	return (port);
685 }
686 
doport(ftp_t * x,char * command,char * par)687 int doport(ftp_t *x, char *command, char *par)
688 {
689 	int	c, rc;
690 	char	*p, line[200];
691 	dtc_t	*ch;
692 
693 	ch = &x->ch;
694 	_getipnum(par, &p, ch->client.ipnum, sizeof(ch->client.ipnum));
695 	ch->client.port = _getport(p, &p);
696 	if (debug)
697 		fprintf (stderr, "client listens on %s:%u\n", ch->client.ipnum, ch->client.port);
698 
699 	get_interface_info(x->fd.server, ch->outside.ipnum, sizeof(ch->outside.ipnum));
700 	ch->osock = bind_to_port(ch->outside.ipnum, 0);
701 	ch->outside.port = get_interface_info(ch->osock, line, sizeof(line));
702 	if (debug)
703 		fprintf (stderr, "listening on %s:%u\n", ch->outside.ipnum, ch->outside.port);
704 
705 	copy_string(line, ch->outside.ipnum, sizeof(line));
706 	for (p=line; (c = *p) != 0; p++) {
707 		if (c == '.')
708 			*p = ',';
709 		}
710 
711 	*p++ = ',';
712 	snprintf (p, 20, "%u,%u", ch->outside.port >> 8, ch->outside.port & 0xFF);
713 
714 
715 	/* Open port first */
716 	ch->isock     = -1;
717 	ch->mode      = MODE_PORT;
718 	ch->state     = PORT_LISTEN;
719 
720 	/* then send PORT cmd */
721 	rc = sfputc(x, "PORT", line, line, sizeof(line), &p);
722 
723 	/* check return code */
724 	if (rc != 200){
725 		cfputs(x, "500 not accepted");
726 		close_ch(x, &x->ch);
727 		}
728 	else
729 		cfputs(x, "200 ok, port allocated");
730 
731 
732 
733 /*	if (rc != 200)
734 		cfputs(x, "500 not accepted");
735 	else {
736 		cfputs(x, "200 ok, port allocated");
737 
738 		ch->isock  = -1;
739 		ch->mode   = MODE_PORT;
740 		ch->state  = PORT_LISTEN;
741 		}
742 */
743 
744 	*ch->command = 0;
745 	return (rc);
746 }
747 
dopasv(ftp_t * x,char * command,char * par)748 int dopasv(ftp_t *x, char *command, char *par)
749 {
750 	int	c, k, rc;
751 	char	*p, line[200];
752 	dtc_t	*ch;
753 
754 	ch = &x->ch;
755 	rc = sfputc(x, "PASV", "", line, sizeof(line), &p);
756 	if (rc != 227) {
757 		cfputs(x, "500 not accepted");
758 		return (0);
759 		}
760 
761 
762 	/*
763 	 * Ende der Port-Koordinaten im Server-Response suchen.
764 	 */
765 
766 	k = strlen(line);
767 	while (k > 0  &&  isdigit(line[k-1]) == 0)
768 		k--;
769 
770 	if (isdigit(line[k-1])) {
771 		line[k--] = 0;
772 		while (k > 0  &&  (isdigit(line[k-1])  ||  line[k-1] == ','))
773 			k--;
774 		}
775 
776 	/*
777 	 * line[k] sollte jetzt auf die erste Ziffer des PASV Response
778 	 * zeigen.
779 	 */
780 
781 	if (isdigit(line[k]) == 0) {
782 		syslog(LOG_NOTICE, "can't locate passive response: %s", line);
783 		cfputs(x, "500 not accepted");
784 		return (0);
785 		}
786 
787 	/*
788 	 * Auslesen der PASV IP-Nummer und des Ports.
789 	 */
790 
791 	p = &line[k];
792 	_getipnum(p, &p, ch->server.ipnum, sizeof(ch->server.ipnum));
793 	ch->server.port = _getport(p, &p);
794 	if (debug)
795 		fprintf (stderr, "server listens on %s:%u\n", ch->server.ipnum, ch->server.port);
796 
797 	get_interface_info(0, ch->inside.ipnum, sizeof(ch->inside.ipnum));
798 	ch->isock = bind_to_port(ch->inside.ipnum, 0);
799 	ch->inside.port = get_interface_info(ch->isock, line, sizeof(line));
800 	if (debug)
801 		fprintf (stderr, "listening on %s:%u\n", ch->inside.ipnum, ch->inside.port);
802 
803 	snprintf (line, sizeof(line) - 2, "227 Entering Passive Mode (%s,%u,%u)",
804 			ch->inside.ipnum,
805 			ch->inside.port >> 8, ch->inside.port & 0xFF);
806 	for (p=line; (c = *p) != 0; p++) {
807 		if (c == '.')
808 			*p = ',';
809 		}
810 
811 	cfputs(x, line);
812 	ch->osock = -1;
813 	ch->mode  = MODE_PASSIVE;
814 	ch->state = PORT_LISTEN;
815 
816 	*ch->command = 0;
817 	ch->operation = 0;
818 
819 	return (rc);
820 }
821 
822 
dofeat(ftp_t * x)823 int dofeat(ftp_t *x)
824 {
825 	/*
826 	 * Not so easy because we have to align with the server response.
827 	 */
828 
829 	int	rc;
830 	char	*p, word[80], serverfeature[80], line[300];
831 	static char *proxyfeatlist = "SIZE:MDTM";
832 
833 	sfputs(x, "%s", "FEAT");
834 	if (sfgets(x, line, sizeof(line)) == NULL) {
835 		syslog(LOG_NOTICE, "monitor: server not responding");
836 		exit (1);
837 		}
838 
839 	rc = atoi(line);
840 	if (rc != 211) {
841 		/* kein FEAT Support */ ;
842 		cfputs(x, "502 command not implemented");
843 		return (1);
844 		}
845 
846 
847 	cfputs(x, "211-feature list follows");
848 	while (1) {
849 		if (sfgets(x, line, sizeof(line)) == NULL) {
850 			syslog(LOG_NOTICE, "lost server in FEAT response");
851 			exit (1);
852 			}
853 		else if (*line != ' ') {
854 
855 			/*
856 			 * RFC2389 specifies exactly one space in this
857 			 * multi-line response.  Nothing else.
858 			 */
859 
860 			break;
861 			}
862 
863 
864 		/* Get feature from server response ...
865 		 */
866 
867 		copy_string(serverfeature, line, sizeof(serverfeature));
868 		strupr(serverfeature);
869 
870 
871 		/* ... and compare it against our feature list
872 		 */
873 
874 
875 		p = proxyfeatlist;
876 		while (*get_quoted(&p, ':', word, sizeof(word)) != 0) {
877 			if (strcmp(word, serverfeature) == 0) {
878 				snprintf (line, sizeof(line) - 4, " %s", word);
879 				cfputs(x, line);
880 				break;
881 				}
882 			}
883 		}
884 
885 	cfputs(x, "211 end");
886 	return (0);
887 }
888 
setvar(ftp_t * x,char * var,char * value)889 int setvar(ftp_t *x, char *var, char *value)
890 {
891 	char	varname[200];
892 
893 	#if defined SOLARIS
894 	snprintf (varname, sizeof(varname) - 2, "%s%s=%s", x->config->varname, var, value != NULL? value: "");
895 	putenv(varname);
896 	#else
897 	snprintf (varname, sizeof(varname) - 2, "%s%s", x->config->varname, var);
898 	setenv(varname, value != NULL? value: "", 1);
899 	#endif
900 
901 	return (0);
902 }
903 
set_variables(ftp_t * x)904 int set_variables(ftp_t *x)
905 {
906 	char	val[200];
907 
908 	setvar(x, "INTERFACE", x->interface);
909 	snprintf (val, sizeof(val) - 2, "%u", x->port);
910 	setvar(x, "PORT", val);
911 
912 	setvar(x, "CLIENT", x->client_ip);
913 	setvar(x, "CLIENTNAME", x->client);
914 
915 	setvar(x, "SERVER", x->server.ipnum);
916 	snprintf (val, sizeof(val) - 2, "%u", x->server.port);
917 	setvar(x, "SERVERPORT", val);
918 
919 	setvar(x, "SERVERNAME", x->server.name);
920 	setvar(x, "SERVERLOGIN", x->username);
921 	setvar(x, "USERNAME", x->local.username);
922 	setvar(x, "PASSWD", x->local.password);
923 
924 	return (0);
925 }
926 
run_acp(ftp_t * x)927 int run_acp(ftp_t *x)
928 {
929 	int	rc, pid, pfd[2];
930 	char	line[300];
931 
932 	if (*x->config->acp == 0)
933 		return (0);
934 
935 	rc = 0;
936 	if (pipe(pfd) != 0) {
937 		syslog(LOG_NOTICE, "-ERR: can't pipe: %s", strerror(errno));
938 		exit (1);
939 		}
940 	else if ((pid = fork()) < 0) {
941 		syslog(LOG_NOTICE, "-ERR: can't fork acp: %s", strerror(errno));
942 		exit (1);
943 		}
944 	else if (pid == 0) {
945 		int	argc;
946 		char	*argv[32];
947 
948 		close(0);		/* Das acp kann nicht vom client lesen. */
949 		dup2(pfd[1], 2);	/* stderr wird vom parent gelesen. */
950 		close(pfd[0]);
951 		set_variables(x);
952 
953 		copy_string(line, x->config->acp, sizeof(line));
954 		argc = split(line, argv, ' ', 30);
955 		argv[argc] = NULL;
956 		execvp(argv[0], argv);
957 
958 		syslog(LOG_NOTICE, "-ERR: can't exec acp %s: %s", argv[0], strerror(errno));
959 		exit (1);
960 		}
961 	else {
962 		int	len;
963 		char	message[300];
964 
965 		close(pfd[1]);
966 		*message = 0;
967 		if ((len = read(pfd[0], message, sizeof(message) - 2)) < 0)
968 			len = 0;
969 
970 		message[len] = 0;
971 		noctrl(message);
972 		close(pfd[0]);
973 
974 		if (waitpid(pid, &rc, 0) < 0) {
975 			syslog(LOG_NOTICE, "-ERR: error while waiting for acp: %s", strerror(errno));
976 			exit (1);
977 			}
978 
979 		rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1;
980 		if (*message == 0)
981 			copy_string(message, rc == 0? "access granted": "access denied", sizeof(message));
982 
983 		if (*message != 0)
984 			syslog(LOG_NOTICE, "%s (rc= %d)", message, rc);
985 		}
986 
987 	return (rc);
988 }
989 
getvarname(char ** here,char * var,int size)990 static char *getvarname(char **here, char *var, int size)
991 {
992 	int	c, k;
993 
994 	size = size - 2;
995 	k = 0;
996 	while ((c = **here) != 0) {
997 		*here += 1;
998 		if (c == ' '  ||  c == '\t'  ||  c == '=')
999 			break;
1000 
1001 		if (k < size)
1002 			var[k++] = c;
1003 		}
1004 
1005 	var[k] = 0;
1006 	strupr(var);
1007 	*here = skip_ws(*here);
1008 
1009 	return (var);
1010 }
1011 
run_ctp(ftp_t * x)1012 int run_ctp(ftp_t *x)
1013 {
1014 	int	rc, pid, pfd[2];
1015 	char	line[300];
1016 	FILE	*fp;
1017 
1018 	if (*x->config->ctp == 0)
1019 		return (0);
1020 
1021 	rc = 0;
1022 	if (pipe(pfd) != 0) {
1023 		syslog(LOG_NOTICE, "-ERR: can't pipe: %s", strerror(errno));
1024 		exit (1);
1025 		}
1026 	else if ((pid = fork()) < 0) {
1027 		syslog(LOG_NOTICE, "-ERR: can't fork trp: %s", strerror(errno));
1028 		exit (1);
1029 		}
1030 	else if (pid == 0) {
1031 		int	argc;
1032 		char	*argv[32];
1033 
1034 		close(0);		/* Das trp kann nicht vom client lesen. */
1035 		dup2(pfd[1], 1);	/* stdout wird vom parent gelesen. */
1036 		close(pfd[0]);
1037 		set_variables(x);
1038 
1039 		copy_string(line, x->config->ctp, sizeof(line));
1040 		argc = split(line, argv, ' ', 30);
1041 		argv[argc] = NULL;
1042 		execvp(argv[0], argv);
1043 
1044 		syslog(LOG_NOTICE, "-ERR: can't exec trp %s: %s",
1045 			argv[0], strerror(errno));
1046 		exit (1);
1047 		}
1048 	else {
1049 		char	*p, var[80], line[300];
1050 
1051 		close(pfd[1]);
1052 		fp = fdopen(pfd[0], "r");
1053 		while (fgets(line, sizeof(line), fp)) {
1054 			p = skip_ws(noctrl(line));
1055 			getvarname(&p, var, sizeof(var));
1056 
1057 			if (strcmp(var, "SERVERNAME") == 0  ||  strcmp(var, "SERVER") == 0)
1058 				copy_string(x->server.name, p, sizeof(x->server.name));
1059 			else if (strcmp(var, "SERVERLOGIN") == 0  ||  strcmp(var, "LOGIN") == 0)
1060 				copy_string(x->username, p, sizeof(x->username));
1061 			else if (strcmp(var, "SERVERPASSWD") == 0  ||  strcmp(var, "PASSWD") == 0)
1062 				copy_string(x->password, p, sizeof(x->password));
1063 			else if (strcmp(var, "SERVERPORT") == 0  ||  strcmp(var, "PORT") == 0)
1064 				x->server.port = atoi(p);
1065 
1066 			/*
1067 			 * Enable the trp to send error messages.
1068 			 */
1069 
1070 			else if (strcmp(var, "-ERR") == 0  ||  strcmp(var, "-ERR:") == 0) {
1071 				syslog(LOG_NOTICE, "-ERR: %s", skip_ws(p));
1072 				exit (1);
1073 				}
1074 			}
1075 
1076 		fclose(fp);
1077 
1078 		/*
1079 		 * In standalone mode we do not receive the SIGCHLD because
1080 		 * we set it to SIG_IGN -- 030406asg
1081 		 */
1082 
1083 		if (x->config->standalone == 0  &&  waitpid(pid, &rc, 0) < 0) {
1084 			syslog(LOG_NOTICE, "-ERR: error while waiting for trp: %s", strerror(errno));
1085 			exit (1);
1086 			}
1087 
1088 		rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1;
1089 		if (rc != 0) {
1090 			syslog(LOG_NOTICE, "-ERR: trp signals error condition, rc= %d", rc);
1091 			exit (1);
1092 			}
1093 		}
1094 
1095 	return (rc);
1096 }
1097 
get_ftpdir(ftp_t * x)1098 int get_ftpdir(ftp_t *x)
1099 {
1100 	int	rc, len;
1101 	char	*p, *start, line[300];
1102 	static char *quotes = "'\"'`";
1103 
1104 	sfputs(x, "%s", "PWD");
1105 	if (sfgets(x, line, sizeof(line)) == NULL) {
1106 		syslog(LOG_NOTICE, "monitor: server not responding");
1107 		exit (1);
1108 		}
1109 
1110 	rc = strtol(line, &p, 10);
1111 	if (rc != 257) {
1112 		syslog(LOG_NOTICE, "monitor: PWD status: %d", rc);
1113 		exit (1);
1114 		}
1115 
1116 	p = skip_ws(p);
1117 	if (*p == 0) {
1118 		syslog(LOG_NOTICE, "monitor: directory unset");
1119 		exit (1);
1120 		}
1121 
1122 
1123 	if ((start = strchr(p, '/')) == NULL) {
1124 		syslog(LOG_NOTICE, "monitor: can't find directory in string: %s", p);
1125 		exit (1);
1126 		}
1127 
1128 	get_word(&start, x->cwd, sizeof(x->cwd));
1129 	if ((len = strlen(x->cwd)) > 0  &&  strchr(quotes, x->cwd[len-1]) != NULL)
1130 		x->cwd[len - 1] = 0;
1131 
1132 	if (*x->cwd != '/') {
1133 		syslog(LOG_NOTICE, "monitor: invalid path: %s", x->cwd);
1134 		exit (1);
1135 		}
1136 
1137 	syslog(LOG_NOTICE, "cwd: %s", x->cwd);
1138 	return (0);
1139 }
1140 
get_ftppath(ftp_t * x,char * path)1141 int get_ftppath(ftp_t *x, char *path)
1142 {
1143 	int	i, k, n, m;
1144 	char	cwp[200], ftpdir[200], pbuf[200];
1145 	char	*part[DIR_MAXDEPTH+5], *dir[DIR_MAXDEPTH+5];
1146 
1147 	/*
1148 	 * Zuerst wird das aktuelle Verzeichnis (der ftppath) in seine
1149 	 * Einzelteile zerlegt ...
1150 	 */
1151 
1152 	if (*path == '/') {
1153 
1154 		/*
1155 		 * ... Ausnahme: die path-Angabe ist absolut ...
1156 		 */
1157 
1158 		dir[0] = "";
1159 		n = 1;
1160 		}
1161 	else {
1162 		copy_string(ftpdir, x->cwd, sizeof(ftpdir));
1163 		if (*ftpdir != 0  &&  strcmp(ftpdir, "/") != 0)
1164 			n = split(ftpdir, part, '/', DIR_MAXDEPTH);
1165 		else {
1166 			dir[0] = "";
1167 			n = 1;
1168 			}
1169 		}
1170 
1171 	/*
1172 	 * ... danach der path.  Die path Teile werden unter Beachtung
1173 	 * der ueblichen Regeln an die Teile des aktuellen Verzeichnisses
1174 	 * angehangen ...
1175 	 */
1176 
1177 	copy_string(pbuf, path, sizeof(pbuf));
1178 	m = split(pbuf, dir, '/', 15);
1179 	for (i=0; i<m; i++) {
1180 		if (*dir[i] == 0)
1181 			/* zwei aufeinander folgende `/' */ ;
1182 		else if (strcmp(dir[i], ".") == 0)
1183 			/* nichts */ ;
1184 		else if (strcmp(dir[i], "..") == 0) {
1185 			if (n > 1)
1186 				n = n - 1;
1187 			}
1188 		else
1189 			part[n++] = dir[i];
1190 
1191 		if (n < 1  ||  n >= DIR_MAXDEPTH)
1192 			return (1);		/* ungueltiges Verzeichnis */
1193 		}
1194 
1195 	/*
1196 	 * ... und das Ergebnis wieder zusammengesetzt.
1197 	 */
1198 
1199 	if (n <= 1) {
1200 		strcpy(cwp, "/");
1201 		}
1202 	else {
1203 		k = 0;
1204 		for (i=1; i<n; i++) {
1205 			if ((k + strlen(part[i]) + 1 + 2) >= sizeof(dir))
1206 				return (1);		/* Name zu lang */
1207 
1208 			cwp[k++] = '/';
1209 			strcpy(&cwp[k], part[i]);
1210 			k += strlen(&cwp[k]);
1211 			}
1212 
1213 		cwp[k] = 0;
1214 		}
1215 
1216 	/*
1217 	 * Der normalisierte path auf das Objekt (Datei oder Verzeichnis,
1218 	 * ist hier egal) ist fertig.
1219 	 */
1220 
1221 	copy_string(x->filepath, cwp, sizeof(x->filepath));
1222 	return (0);
1223 }
1224 
run_ccp(ftp_t * x,char * cmd,char * par)1225 int run_ccp(ftp_t *x, char *cmd, char *par)
1226 {
1227 	int	rc, pid, pfd[2], lfd[2];
1228 	char	message[300], line[300];
1229 
1230 	/*
1231 	 * Wenn kein ccp angegeben ist ist alles erlaubt.
1232 	 */
1233 
1234 	if (*x->config->ccp == 0)
1235 		return (CCP_OK);
1236 
1237 
1238 	/*
1239 	 * Der Vorgang fuer ccp's ist fast gleich mit dem fuer acp's.
1240 	 */
1241 
1242 	rc = 0;
1243 	if (pipe(pfd) != 0  ||  pipe(lfd)) {
1244 		syslog(LOG_NOTICE, "-ERR: can't pipe: %s", strerror(errno));
1245 		exit (1);
1246 		}
1247 	else if ((pid = fork()) < 0) {
1248 		syslog(LOG_NOTICE, "-ERR: can't fork ccp: %s", strerror(errno));
1249 		exit (1);
1250 		}
1251 	else if (pid == 0) {
1252 		int	argc;
1253 		char	*argv[32];
1254 
1255 		dup2(pfd[1], 2);	/* stderr nach FTP Client */
1256 		close(pfd[0]);
1257 
1258 		dup2(lfd[1], 1);	/* stdout nach syslog */
1259 		close(lfd[0]);
1260 
1261 		close(0);
1262 		set_variables(x);
1263 
1264 		setvar(x, "COMMAND", cmd);
1265 		setvar(x, "PARAMETER", par);
1266 
1267 		setvar(x, "SESSION", x->session);
1268 		snprintf (line, sizeof(line) - 2, "%d", x->ccpcoll);
1269 		setvar(x, "CCPCOLL", line);
1270 
1271 		setvar(x, "FTPHOME", x->home);
1272 		setvar(x, "FTPPATH", x->filepath);
1273 
1274 		copy_string(line, x->config->ccp, sizeof(line));
1275 		argc = split(line, argv, ' ', 30);
1276 		argv[argc] = NULL;
1277 		execvp(argv[0], argv);
1278 
1279 		syslog(LOG_NOTICE, "-ERR: can't exec ccp %s: %s", argv[0], strerror(errno));
1280 		exit (1);
1281 		}
1282 	else {
1283 		int	len;
1284 
1285 		/*
1286 		 * Nicht gebrauchte fd's schliessen.
1287 		 */
1288 
1289 		close(pfd[1]);
1290 		close(lfd[1]);
1291 
1292 
1293 		/*
1294 		 * syslog Meldung lesen und entsprechende pipe schliessen.
1295 		 */
1296 
1297 		*message = 0;
1298 		if ((len = read(lfd[0], message, sizeof(message) - 2)) < 0)
1299 			len = 0;
1300 
1301 		message[len] = 0;
1302 		noctrl(message);
1303 		close(lfd[0]);
1304 
1305 		if (*message != 0)
1306 			syslog(LOG_NOTICE, "%s", message);
1307 
1308 
1309 
1310 		/*
1311 		 * Fehlermeldung lesen, pipe schliessen.
1312 		 */
1313 
1314 		*message = 0;
1315 		if ((len = read(pfd[0], message, sizeof(message) - 2)) < 0)
1316 			len = 0;
1317 
1318 		message[len] = 0;
1319 		noctrl(message);
1320 		close(pfd[0]);
1321 
1322 
1323 		/*
1324 		 * return code holen.
1325 		 */
1326 
1327 		if (waitpid(pid, &rc, 0) < 0) {
1328 			syslog(LOG_NOTICE, "-ERR: error while waiting for ccp: %s", strerror(errno));
1329 			exit (1);
1330 			}
1331 
1332 		rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1;
1333 		if (rc == 0)
1334 			return (CCP_OK);
1335 
1336 		if (*message == 0)
1337 			copy_string(message, "permission denied", sizeof(message));
1338 
1339 /*
1340  *		snprintf (command, sizeof(command) - 2, "%s%s%s", cmd, (par != 0? " ": ""), par);
1341  *		syslog(LOG_NOTICE, "ccp: -ERR: %s@%s: %s: %s: rc= %d",
1342  *				x->username, x->server.name,
1343  *				command, message, rc);
1344  */
1345 		}
1346 
1347 	x->ccpcoll++;
1348 	if (isdigit(*message))
1349 		cfputs(x, message);
1350 	else {
1351 		snprintf (line, sizeof(line) - 2, "553 %s", message);
1352 		cfputs(x, line);
1353 		}
1354 
1355 /*	cfputs(x, "553 permission denied."); */
1356 
1357 	return (CCP_ERROR);
1358 }
1359 
1360 
1361 	/*
1362 	 * dologin() accepts now blanks with in and at the end of
1363 	 * passwords - 22JAN02asg
1364 	 */
1365 
dologin(ftp_t * x)1366 int dologin(ftp_t *x)
1367 {
1368 	int	c, i, rc;
1369 	char	*p, word[80], line[300];
1370 	struct hostent *hostp;
1371 	struct sockaddr_in saddr;
1372 
1373 	while (1) {
1374 		if (readline_fd(x, 0, line, sizeof(line)) == NULL)
1375 			return (1);
1376 
1377 		if (x->config->allow_passwdblanks == 0)
1378 			p = noctrl(line);
1379 		else {
1380 			p = line;
1381 			for (i=strlen(line)-1; i>=0; i--) {
1382 				if ((c = line[i]) != '\n'  &&  c != '\r') {
1383 					line[i+1] = 0;
1384 					break;
1385 					}
1386 				}
1387 			}
1388 
1389 		get_word(&p, word, sizeof(word));
1390 		strupr(word);
1391 		if (strcmp(word, "USER") == 0) {
1392 			get_word(&p, x->username, sizeof(x->username));
1393 			cfputs(x, "331 password required");
1394 			}
1395 		else if (strcmp(word, "PASS") == 0) {
1396 			if (*x->username == 0) {
1397 				cfputs(x, "503 give USER first");
1398 				continue;
1399 				}
1400 
1401 			if (x->config->allow_passwdblanks == 0)
1402 				get_word(&p, x->password, sizeof(x->password));
1403 			else
1404 				copy_string(x->password, p, sizeof(x->password));
1405 
1406 			break;
1407 			}
1408 		else if (strcmp(word, "QUIT") == 0) {
1409 			cfputs(x, "221 goodbye");
1410 			return (2);
1411 			}
1412 		else {
1413 			cfputs(x, "530 login first");
1414 			}
1415 		}
1416 
1417 
1418 	if (*x->config->ctp != 0) {
1419 
1420 		/*
1421 		 * We are extremly liberate here with server selection
1422 		 * if we have a dynamic control program, we accept
1423 		 * anything here -- 030404asg
1424 		 */
1425 
1426 		if ((p = strchr(x->username, '@')) == NULL  &&  (p = strchr(x->username, '%')) == NULL)
1427 			*x->server.name = 0;
1428 		else if (x->config->use_last_at == 0) {
1429 			*p++ = 0;
1430 			copy_string(x->server.name, p, sizeof(x->server.name));
1431 			}
1432 		else {
1433 			if ((p = strrchr(x->username, '@')) == NULL)
1434 				p = strrchr(x->username, '%');
1435 
1436 			*p++ = 0;
1437 			copy_string(x->server.name, p, sizeof(x->server.name));
1438 			}
1439 		}
1440 	else if (x->config->selectserver == 0) {
1441 		if ((p = strchr(x->username, '@')) != NULL  &&  (p = strchr(x->username, '%')) != NULL) {
1442 			cfputs(x, "500 service unavailable");
1443 			syslog(LOG_NOTICE, "-ERR: hostname supplied: %s", p);
1444 			exit (1);
1445 			}
1446 
1447 		copy_string(x->server.name, x->config->server, sizeof(x->server.name));
1448 		}
1449 	else {
1450 
1451 		/*
1452 		 * Normally we search for the first '@' so that the client can
1453 		 * not use "proxy hopping". The option "-u" can override
1454 		 * this behaviour.
1455 		 */
1456 
1457 		if (x->config->use_last_at == 0) {
1458 			if ((p = strchr(x->username, '@')) == NULL  &&  (p = strchr(x->username, '%')) == NULL) {
1459 				cfputs(x, "500 service unavailable");
1460 				syslog(LOG_NOTICE, "-ERR: missing hostname");
1461 				exit (1);
1462 				}
1463 			}
1464 		else {
1465 			if ((p = strrchr(x->username, '@')) == NULL  &&  (p = strrchr(x->username, '%')) == NULL) {
1466 				cfputs(x, "500 service unavailable");
1467 				syslog(LOG_NOTICE, "-ERR: missing hostname");
1468 				exit (1);
1469 				}
1470 			}
1471 
1472 
1473 		*p++ = 0;
1474 		copy_string(x->server.name, p, sizeof(x->server.name));
1475 
1476 		/*
1477 		 * Den Server auf der Serverliste suchen, wenn eine Liste
1478 		 * vorhanden ist.
1479 		 */
1480 
1481 /*
1482  * Checking the server against the given list is done later now,
1483  * see below.  Code quoted -- 030404asg
1484  *
1485  *		if ((p = x->config->serverlist) != NULL  &&  *p != 0) {
1486  *			int	permitted;
1487  *			char	pattern[80];
1488  *
1489  *			permitted = 0;
1490  *			while ((p = skip_ws(p)), *get_quoted(&p, ',', pattern, sizeof(pattern)) != 0) {
1491  *				noctrl(pattern);
1492  *				if (strpcmp(x->server.name, pattern) == 0) {
1493  *					permitted = 1;
1494  *					break;
1495  *					}
1496  *				}
1497  *
1498  *			if (permitted == 0) {
1499  *				cfputs(x, "500 service unavailable");
1500  *				syslog(LOG_NOTICE, "-ERR: hostname not permitted: %s", x->server.name);
1501  *				exit (1);
1502  *				}
1503  *			}
1504  */
1505 		}
1506 
1507 
1508 
1509 	/*
1510 	 * Wenn vorhanden Proxy Login und Passwort auslesen.
1511 	 */
1512 
1513 	if ((p = strchr(x->username, ':')) != NULL) {
1514 		*p++ = 0;
1515 		copy_string(x->local.username, x->username, sizeof(x->local.username));
1516 		copy_string(x->username, p, sizeof(x->username));
1517 		}
1518 
1519 	if ((p = strchr(x->password, ':')) != NULL) {
1520 		*p++ = 0;
1521 		copy_string(x->local.password, x->password, sizeof(x->local.password));
1522 		copy_string(x->password, p, sizeof(x->password));
1523 		}
1524 
1525         /*
1526          * Call the dynamic configuration program.
1527          */
1528 
1529         if (*x->config->ctp != 0) {
1530 		x->server.port = get_port(x->server.name, 21);
1531 
1532                 if (run_ctp(x) != 0)
1533                         exit (0);       /* Never happens, we exit in run_ctp() */
1534 
1535 		if (debug != 0) {
1536 	                fprintf (stderr, "trp debug: server= %s:%u, login= %s, passwd= %s",
1537 					x->server.name, x->server.port,
1538 					x->username, x->password);
1539 			}
1540                 }
1541 
1542 
1543 	/*
1544 	 * Get port an IP number of server.  Moved code here -- 030404asg
1545 	 */
1546 
1547 	x->server.port = get_port(x->server.name, 21);
1548 	if ((hostp = gethostbyname(x->server.name)) == NULL) {
1549 		cfputs(x, "500 service unavailable");
1550 		syslog(LOG_NOTICE, "-ERR: can't resolve hostname: %s", x->server.name);
1551 		exit (1);
1552 		}
1553 
1554 	memcpy(&saddr.sin_addr, hostp->h_addr, hostp->h_length);
1555 	copy_string(x->server.ipnum, inet_ntoa(saddr.sin_addr), sizeof(x->server.ipnum));
1556 
1557 
1558 	/*
1559 	 * Call the access control program to check if the proxy
1560 	 * request is allowed.  Moved code here -- 030404asg
1561 	 */
1562 
1563 	if (*x->config->acp != 0) {
1564 		if (run_acp(x) != 0)
1565 			exit (0);
1566 		}
1567 
1568 
1569 	/*
1570 	 * Verification if the destination server is on the given list
1571 	 * is done now here.
1572 	 *
1573 	 * Notice: Prior to this change you could give a fixed desination
1574 	 * server as command line argument and a list of allowed server
1575 	 * too.  Meaningless because the proxy didn't care when the `server
1576 	 * selection' option wasn't turned on.  Now also the fixed server
1577 	 * is checked against the list.
1578 	 *
1579 	 * I don't expect that this breaks an already running configuration
1580 	 * because as said above this configuration was senseless in earlier
1581 	 * proxy versions -- 030404asg
1582 	 */
1583 
1584 	if ((p = x->config->serverlist) != NULL  &&  *p != 0) {
1585 		int	permitted;
1586 		char	pattern[80];
1587 
1588 		permitted = 0;
1589 		while ((p = skip_ws(p)), *get_quoted(&p, ',', pattern, sizeof(pattern)) != 0) {
1590 			noctrl(pattern);
1591 			if (strpcmp(x->server.name, pattern) == 0) {
1592 				permitted = 1;
1593 				break;
1594 				}
1595 			}
1596 
1597 		if (permitted == 0) {
1598 			cfputs(x, "500 service unavailable");
1599 			syslog(LOG_NOTICE, "-ERR: hostname not permitted: %s", x->server.name);
1600 			exit (1);
1601 			}
1602 		}
1603 
1604 
1605 	/*
1606 	 * Establish connection to the server
1607 	 */
1608 
1609 	if ((x->fd.server = openip(x->server.name, x->server.port, x->config->sourceip, 0)) < 0) {
1610 		cfputs(x, "500 service unavailable");
1611 		syslog(LOG_NOTICE, "-ERR: can't connect to server: %s", x->server.name);
1612 		exit (1);
1613 		}
1614 
1615 	syslog(LOG_NOTICE, "connected to server: %s", x->server.name);
1616 
1617 
1618 	if (sfputc(x, NULL, NULL, line, sizeof(line), NULL) != 220) {
1619 		cfputs(x, "500 service unavailable");
1620 		syslog(LOG_NOTICE, "-ERR: unexpected server greeting: %s", line);
1621 		exit (1);
1622 		}
1623 
1624 	/*
1625 	 * Login auf FTP-Server.
1626 	 *
1627 	 * Complete rewrite because of servers wanting no password after
1628 	 * login of anonymous user.
1629 	 */
1630 
1631 	rc = sfputc(x, "USER", x->username, line, sizeof(line), NULL);
1632 
1633 	if (rc == 230) {
1634 		cfputs(x, "230 login accepted");
1635 		syslog(LOG_NOTICE, "login accepted: %s@%s, no password needed.", x->username, x->server.name);
1636 		return (0);
1637 		}
1638 	else if (rc != 331) {
1639 		cfputs(x, "500 service unavailable");
1640 		syslog(LOG_NOTICE, "-ERR: unexpected reply to USER: %s", line);
1641 		exit (1);
1642 		}
1643 	else if (sfputc(x, "PASS", x->password, line, sizeof(line), NULL) != 230) {
1644 		cfputs(x, "530 bad login");
1645 		syslog(LOG_NOTICE, "-ERR: reply to PASS: %s", line);
1646 		exit (1);
1647 		}
1648 
1649 	cfputs(x, "230 login accepted");
1650 	syslog(LOG_NOTICE, "login accepted: %s@%s", x->username, x->server.name);
1651 
1652 	return (0);
1653 
1654 /*
1655 	if (sfputc(x, "USER", x->username, line, sizeof(line), NULL) != 331) {
1656 		cfputs(x, "500 service unavailable");
1657 		syslog(LOG_NOTICE, "-ERR: unexpected reply to USER: %s", line);
1658 		exit (1);
1659 		}
1660 	else if (sfputc(x, "PASS", x->password, line, sizeof(line), NULL) != 230) {
1661 		cfputs(x, "530 bad login");
1662 		syslog(LOG_NOTICE, "-ERR: reply to PASS: %s", line);
1663 		exit (1);
1664 		}
1665 
1666 	cfputs(x, "230 login accepted");
1667 	syslog(LOG_NOTICE, "login accepted: %s@%s", x->username, x->server.name);
1668 
1669 	return (0);
1670 */
1671 
1672 
1673 }
1674 
1675 
1676 
signal_handler(int sig)1677 void signal_handler(int sig)
1678 {
1679 	/*
1680 	 * Changed the way we handle broken pipes (broken control or
1681 	 * data connection).  We ignore it here but write() returns -1
1682 	 * and errno is set to EPIPE which is checked.
1683 	 */
1684 
1685 	if (sig == SIGPIPE) {
1686 		signal(SIGPIPE, signal_handler);
1687 		return;
1688 		}
1689 
1690 	syslog(LOG_NOTICE, "-ERR: received signal #%d", sig);
1691 	exit (1);
1692 }
1693 
set_signals(void)1694 int set_signals(void)
1695 {
1696 	signal(SIGHUP, signal_handler);
1697 	signal(SIGINT, signal_handler);
1698 	signal(SIGQUIT, signal_handler);
1699 	signal(SIGSEGV, signal_handler);
1700 	signal(SIGPIPE, signal_handler);
1701 	signal(SIGALRM, signal_handler);
1702 	signal(SIGTERM, signal_handler);
1703 	signal(SIGUSR1, signal_handler);
1704 	signal(SIGUSR2, signal_handler);
1705 
1706 	return (0);
1707 }
1708 
1709 
getcmd(char * name)1710 ftpcmd_t *getcmd(char *name)
1711 {
1712 	int	i;
1713 
1714 	for (i=0; cmdtab[i].name[0] != 0; i++) {
1715 		if (strcmp(cmdtab[i].name, name) == 0)
1716 			return (&cmdtab[i]);
1717 		}
1718 
1719 	return (NULL);
1720 }
1721 
1722 
proxy_request(config_t * config)1723 int proxy_request(config_t *config)
1724 {
1725 	int	rc;
1726 	char	*p, command[200], parameter[200], line[300];
1727 	ftpcmd_t *cmd;
1728 	ftp_t	*x;
1729 
1730 	set_signals();
1731 
1732 	/*
1733 	 * Set socket options to prevent us from the rare case that
1734 	 * we transfer data to/from the client before the client has
1735 	 * seen our "150 ..." message.
1736 	 * Seems so that is doesn't work on all systems.
1737 	 * So temporary only enable it on linux.
1738 	 */
1739 
1740 #if defined(__linux__)
1741 
1742 	rc = 1;
1743 	if (setsockopt(1, SOL_TCP, TCP_NODELAY, &rc, sizeof(rc)) != 0)
1744 		syslog(LOG_NOTICE, "can't set TCP_NODELAY, error= %s", strerror(errno));
1745 
1746 #endif
1747 
1748 	if (config->bsize <= 0)
1749 		config->bsize = 1024;
1750 	else if (config->bsize > FTPMAXBSIZE)
1751 		config->bsize = FTPMAXBSIZE;
1752 
1753 	x = allocate(sizeof(ftp_t));
1754 	x->config = config;
1755 	snprintf (x->session, sizeof(x->session) - 2, "%lu-%u", time(NULL), getpid());
1756 
1757 
1758 	/*
1759 	 * Fix potential problems after immediate initial unseccsesful
1760 	 * up/downloads.  Wasn't a problem since we all do a LIST
1761 	 * at first.
1762 	 */
1763 
1764 	x->ch.isock = -1;
1765 	x->ch.osock = -1;
1766 
1767 
1768 	if (get_client_info(x, 0) < 0) {
1769 		syslog(LOG_NOTICE, "-ERR: can't get client info: %s", strerror(errno));
1770 		exit (1);
1771 		}
1772 
1773 	x->port = get_interface_info(0, x->interface, sizeof(x->interface));
1774 	syslog(LOG_NOTICE, "connected to client: %s, interface= %s:%u", x->client,
1775 				x->interface, x->port);
1776 
1777 	if (*x->config->configfile != 0) {
1778 		if (readconfig(x->config, x->config->configfile, x->interface) == 0) {
1779 			cfputs(x, "421 not available");
1780 			syslog(LOG_NOTICE, "-ERR: unconfigured interface: %s", x->interface);
1781 			exit (1);
1782 			}
1783 		}
1784 
1785 	syslog(LOG_NOTICE, "info: monitor mode: %s, ccp: %s",
1786 			x->config->monitor == 0? "off": "on",
1787 			*x->config->ccp == 0? "<unset>": x->config->ccp);
1788 
1789 	cfputs(x, "220 server ready - login please");
1790 	if ((rc = dologin(x)) < 0)
1791 		return (1);
1792 	else if (rc == 2)
1793 		return (0);
1794 
1795 
1796 	/*
1797 	 * Open the xferlog if we have one.
1798 	 */
1799 
1800 	if (*x->config->xferlog != 0) {
1801 		if (*x->server.name == 0)
1802 			copy_string(x->logusername, x->username, sizeof(x->logusername));
1803 		else if (x->server.port != 21)
1804 			snprintf (x->logusername, sizeof(x->logusername), "%s@%s:%u", x->username, x->server.name, x->server.port);
1805 		else
1806 			snprintf (x->logusername, sizeof(x->logusername), "%s@%s", x->username, x->server.name);
1807 
1808 		x->xlfp = fopen(x->config->xferlog, "a");
1809 		if (x->xlfp == NULL) {
1810 			syslog(LOG_NOTICE, "-WARN: can't open xferlog: %s, error= %s",
1811 					x->config->xferlog, strerror(errno));
1812 			}
1813 		}
1814 
1815 	if (x->config->monitor) {
1816 		get_ftpdir(x);
1817 		copy_string(x->home, x->cwd, sizeof(x->home));
1818 		}
1819 
1820 	while ((p = cfgets(x, line, sizeof(line))) != NULL) {
1821 		if (*p == '\001') {
1822 			if (*x->ch.command != 0) {
1823 				syslog(LOG_NOTICE, "%s %s: %ld bytes", x->ch.command, x->ch.filename, x->ch.bytes);
1824 
1825 				if (x->xlfp != NULL) {
1826 					unsigned long now;
1827 					char	date[80];
1828 
1829 					/*
1830 					 * Write xferlog entry but notice that (1) session are never
1831 					 * flagged as anonymous and (2) the transfer type is always
1832 					 * binary (type flag was added to data channel but is
1833 					 * actually not used. 10MAY04wzk
1834 					 */
1835 
1836 					now = time(NULL);
1837 					copy_string(date, ctime(&now), sizeof(date));
1838 					fprintf (x->xlfp, "%s %lu %s %lu %s %c %c %c %c %s %s %d %s %c\n",
1839 							date,
1840 							now - x->ch.started,
1841 							x->client_ip,
1842 							x->ch.bytes,
1843 							x->ch.filename,
1844 							'b',		/* x->ch.type == TYPE_ASC? 'a': 'b', */
1845 							'-',
1846 							strcmp(x->ch.command, "RETR")? 'i': 'o',
1847 							'u',		/* x->isanonymous == 1? 'a': 'u', */
1848 							x->logusername,
1849 							"ftp", 1, x->logusername, 'c');
1850 					fflush(x->xlfp);
1851 					}
1852 				}
1853 
1854 			/*
1855 			 * Handle multiline server responses after the
1856 			 * data transfer.
1857 			 */
1858 
1859 			sfputc(x, NULL, NULL, line, sizeof(line), NULL);
1860 			cfputs(x, line);
1861 
1862 			continue;
1863 			}
1864 
1865 		p = noctrl(line);
1866 		get_word(&p, command, sizeof(command));
1867 		strupr(command);
1868 
1869 		if ((cmd = getcmd(command)) == NULL  ||  cmd->resp == -1) {
1870 			cfputs(x, "502 command not implemented");
1871 			syslog(LOG_NOTICE, "command not implemented: %s", command);
1872 			continue;
1873 			}
1874 
1875 		*x->filepath = 0;
1876 		if (cmd->par == 0)
1877 			*parameter = 0;
1878 		else {
1879 			if (strcmp(command, "CDUP") == 0)
1880 				strcpy(parameter, "..");
1881 			else if (strcmp(command, "SITE") == 0)
1882 				copy_string(parameter, p, sizeof(parameter));
1883 			else {
1884 				if (x->config->allow_blanks != 0)
1885 					copy_string(parameter, p, sizeof(parameter));
1886 				else
1887 					get_word(&p, parameter, sizeof(parameter));
1888 
1889 				if (*parameter == 0) {
1890 					if (strcmp(command, "LIST") == 0  ||  strcmp(command, "NLST") == 0)
1891 						/* nichts, ist ok */ ;
1892 					else {
1893 						syslog(LOG_NOTICE, "parameter required: %s", command);
1894 						exit (1);
1895 						}
1896 					}
1897 				}
1898 
1899 			if (cmd->ispath != 0) {
1900 				if (x->config->monitor) {
1901 					if ((strcmp(command, "LIST") == 0  ||  strcmp(command, "NLST") == 0)
1902 					    &&  *parameter == 0) {
1903 
1904 						/*
1905 						 * Sonderfall: wir simulieren `*' als Parameter.
1906 						 */
1907 
1908 						get_ftppath(x, "*");
1909 						}
1910 					else
1911 						get_ftppath(x, parameter);
1912 					}
1913 				}
1914 			}
1915 
1916 
1917 		if (cmd->useccp != 0) {
1918 			if (run_ccp(x, command, parameter) != CCP_OK)
1919 				continue;
1920 			}
1921 
1922 
1923 		if (strcmp(command, "QUIT") == 0) {
1924 /*		        run_ccp(x, "QUIT", ""); */
1925 			doquit(x);
1926 			break;
1927 			}
1928 		else if (strcmp(command, "PORT") == 0)
1929 			doport(x, command, parameter);
1930 		else if (strcmp(command, "FEAT") == 0)
1931 			dofeat(x);
1932 		else if (strcmp(command, "PASV") == 0)
1933 			dopasv(x, command, parameter);
1934 		else if (strcmp(command, "LIST") == 0  ||  strcmp(command, "NLST") == 0) {
1935 			x->ch.operation = OP_GET;	/* fuer PASV mode */
1936 			rc = sfputc(x, command, parameter, line, sizeof(line), NULL);
1937 			if (rc == 125  ||  rc == 150) {
1938 				x->ch.operation = OP_GET;
1939 				x->ch.seen150   = 1;
1940 				if (debug >= 2)
1941 					fprintf (stderr, "received 150 response\n");
1942 				}
1943 			else
1944 				close_ch(x, &x->ch);
1945 
1946 			cfputs(x, line);
1947 			*x->ch.command = 0;
1948 			}
1949 		else if (strcmp(command, "RETR") == 0) {
1950 			x->ch.operation = OP_GET;	/* fuer PASV mode */
1951 			rc = sfputc(x, "RETR", parameter, line, sizeof(line), NULL);
1952 			if (rc == 125  ||  rc == 150) {
1953 				x->ch.operation = OP_GET;
1954 				x->ch.seen150   = 1;
1955 				if (debug >= 2)
1956 					fprintf (stderr, "received 150 response\n");
1957 				}
1958 			else
1959 				close_ch(x, &x->ch);
1960 
1961 			cfputs(x, line);
1962 			copy_string(x->ch.command, "RETR", sizeof(x->ch.command));
1963 			copy_string(x->ch.filename, x->config->monitor != 0? x->filepath: parameter, sizeof(x->ch.filename));
1964 
1965 			if (extralog != 0)
1966 				syslog(LOG_NOTICE, "%d RETR %s", rc, (x->config->monitor != 0)? parameter: x->filepath);
1967 			}
1968 		else if (strcmp(command, "STOR") == 0  ||  strcmp(command, "APPE") == 0  ||  strcmp(command, "STOU") == 0) {
1969 			x->ch.operation = OP_PUT;	/* fuer PASV mode */
1970 			rc = sfputc(x, command, parameter, line, sizeof(line), NULL);
1971 			if (rc == 125  ||  rc == 150) {
1972 				x->ch.operation = OP_PUT;
1973 				x->ch.seen150   = 1;
1974 				if (debug >= 2)
1975 					fprintf (stderr, "received 150 response\n");
1976 
1977 				copy_string(x->ch.command, command, sizeof(x->ch.command));
1978 				}
1979 			else
1980 				close_ch(x, &x->ch);
1981 
1982 			cfputs(x, line);
1983 			copy_string(x->ch.filename, x->config->monitor != 0? x->filepath: parameter, sizeof(x->ch.filename));
1984 			if (extralog != 0) {
1985 				if (strcmp(command, "STOU") == 0)
1986 					syslog(LOG_NOTICE, "%d %s %s", rc, command, "-");
1987 				else
1988 					syslog(LOG_NOTICE, "%d %s %s", rc, command, x->ch.filename);
1989 				}
1990 			}
1991 		else {
1992 			if (strcmp(command, "CDUP") == 0)
1993 				*parameter = 0;
1994 
1995 			rc = sfputc(x, command, parameter, line, sizeof(line), NULL);
1996 			cfputs(x, line);
1997 			if (extralog != 0  &&  cmd->log != 0) {
1998 				if (x->config->monitor != 0  &&  cmd->ispath != 0)
1999 					syslog(LOG_NOTICE, "%d %s %s", rc, command, x->filepath);
2000 				else
2001 					syslog(LOG_NOTICE, "%d %s%s%s", rc, command, *parameter != 0? " ": "", parameter);
2002 				}
2003 
2004 			if (strcmp(command, "CWD") == 0  ||  strcmp(command, "CDUP") == 0) {
2005 				if (x->config->monitor)
2006 					get_ftpdir(x);
2007 				}
2008 			}
2009 		}
2010 
2011 	if (*x->config->ccp != 0)
2012 		run_ccp(x, "+EXIT", x->session);
2013 
2014 	syslog(LOG_NOTICE, "+OK: proxy terminating");
2015 	return (0);
2016 }
2017 
2018 
2019 
2020