--- AUTHORS +++ AUTHORS 2001/09/19 10:40:42 @@ -11,7 +11,7 @@ Documentation Marius Tomaschewski - Test environment + Test environment, programming Volker Wiegand General program design and programming --- CHANGES.mt +++ CHANGES.mt 2001/09/19 12:23:29 @@ -0,0 +1,54 @@ + + * 1.7tp1 / 01.07.2000: + first implementation of transparent proxying + + * 1.7tp2 / 14.07.2000: + added checks if destination is a local address; + transparent proxying should work fine now + + * 1.7tp3 / 22.09.2000: + fixed ugly daemon-mode bug killing all proxy + instances on QUIT + + * 1.7tp4 / 03.10.2000: + changed log file opening to rename the log file + if it already exists instead of delete it :-) + + * 1.7tp5 / 08.10.2000: + autoconf cleanups - tested on linux, freebsd4.0 + and solaris7. added UseMagicChar config keyword + to support other chars than @ as user separators + in "AllowMagicUser" mode. + + * 1.7tp6 / 05.06.2001: + - added sample rc.config.ftp-proxy and rc.script.ftp-config + - implemented LDAPBindDN, LDAPBindPW for authenticated simple bind + - autoconf ctags, ftp-proxy.conf.sample cleanups + - fixed QUIT to be allowed every time + - fixed pid file creation order to be before UID 0 is dropped + - fixed ActivePort default to be INPORT_ANY (random port) if + not running as UID 0 instead of IPPORT_FTP - 1 (port 20) + an no ActiveMinDataPort/ActiveMaxDataPort given + - fixed (I hope ;-) bind/connect problems (see also SockBindRand option) + - added patch for infinite-loop bug in telnet control handling + - added patch for unneeded exit in socket_sck2addr and on client fork failure + - added patch for clean up the PID of a child in daemon array (MaxClients) + - added contributors to CREDITS file + - updated manual page (ftp-proxy.conf.5.in) + +* 1.7tp7 / 20.06.2001: + - the proxy uses ip address fetched with getsockname from the control + socket to the client for data communications. In transparent mode, + this may cause problems with ip filters, because getsockname returns + the address of the ftp server the client means it is connected to + (see also "netstat -atn" output). + Now, the proxy prefers to use the IP set via listen in the config; + if no Listen is set, ip address from getsockname is used, so "Listen" + and in some environments also TranslatedAddress should be set. + - added socket_sck2addr CTAGS_OPTS typo and SockBindRand config_int + vs. config_bool patch from Andrea. thanx! + +* 1.7tp8 / 19.09.2001: + - fixed sample run level script to be LSB compliant (I hope it is now) + - added Solaris FIONREAD (broken pipe) patch from Stefan Landvoigt + --- CREDITS +++ CREDITS 2001/09/19 12:18:24 @@ -17,3 +17,18 @@ Marc Heuse common: Symlink-protecting debug/log files. +Rogier Wolff + idea and a first implementation of rand-usage for port bind's + +Antoniu-George Savu + patch for infinite-loop bug in telnet control handling + patch for useless exit in socket_sck2addr and on client fork failure + patch for clean up the PID of a child in daemon array (MaxClients) + +Andrea Dell'Amico + patch fixing a socket_sck2addr return value, a CTAGS_OPTS typo in + configure.in, changing config_int in config_bool for SockBindRand + +Stefan Landvoigt + patch for FIONREAD problems on Solaris (broken pipe) + --- common/Makefile.in +++ common/Makefile.in 2001/09/19 10:40:42 @@ -46,6 +46,8 @@ COM_LIB= libcommon.a TAGS= @TAGS@ +CTAGS= @CTAGS@ +CTAG_OPTS= @CTAG_OPTS@ COM_SRCS= com-config.c \ com-debug.c \ @@ -71,7 +73,7 @@ all: $(TAGS) $(COM_LIB) tags: $(COM_SRCS) $(COM_HDRS) - ctags -w $(COM_SRCS) $(COM_HDRS) + $(CTAGS) $(CTAG_OPTS) $(COM_SRCS) $(COM_HDRS) @echo "" $(COM_LIB): $(COM_LIB)($(COM_OBJS)) --- common/com-misc.c +++ common/com-misc.c 2001/09/19 10:40:42 @@ -644,6 +644,37 @@ } +/* ------------------------------------------------------------ ** +** +** Function......: misc_rand +** +** Parameters....: lower range mark +** upper range mark +** +** Return........: random number between lower and upper mark +** +** Purpose.......: generates a random number in specified range. +** +** ------------------------------------------------------------ */ + +int misc_rand (int lrng, int urng) +{ + struct timeval t; + + if (lrng == urng) return lrng; + if (lrng > urng) { + /* huh? swap it */ + lrng ^= urng; + urng ^= lrng; + lrng ^= urng; + } + + gettimeofday (&t, NULL); + srand (t.tv_usec); + + return (lrng + (rand () % (urng - lrng + 1))); +} + /* ------------------------------------------------------------ * $Log: com-misc.c,v $ * Revision 1.6 1999/09/30 09:49:45 wiegand --- common/com-misc.h +++ common/com-misc.h 2001/09/19 10:40:42 @@ -82,7 +82,7 @@ char *misc_strncpy (char *s1, const char *s2, size_t len); void misc_uidgid (uid_t uid, gid_t gid); - +int misc_rand (int lrng, int urng); /* ------------------------------------------------------------ */ --- common/com-socket.c +++ common/com-socket.c 2001/09/19 12:22:54 @@ -69,12 +69,34 @@ #endif #include +#if defined(HAVE_SYS_FILIO_H) +#include +#endif #include -#include -#include +#if defined(HAVE_STROPTS_H) +# include +#endif +#if defined(HAVE_SYS_CONF_H) +# include +#endif +#if defined(HAVE_NETINET_IN_SYSTM_H) +# include +#endif +#if defined (SOLARIS) +# define FIONREAD I_NREAD +#endif +#if defined(HAVE_NETINET_IN_H) +# include +#endif +#if defined(HAVE_NETINET_IP_H) +# include +#endif #include #include +#if defined(HAVE_NET_IF_H) +# include +#endif #if defined(HAVE_SYS_SOCKIO_H) # include @@ -248,7 +270,8 @@ static void socket_accept(void) { - char peer[PEER_LEN]; + char peer[PEER_LEN] = {0}; + char dest[PEER_LEN] = {0}; struct sockaddr_in saddr; int nsock, len; @@ -262,10 +285,16 @@ syslog_error("can't accept client"); return; } - strcpy(peer, inet_ntoa(saddr.sin_addr)); + strncpy(peer, inet_ntoa(saddr.sin_addr), sizeof(peer)-1); + *(peer+sizeof(peer)-1) = 0; + + if( !getsockname(nsock, (struct sockaddr *)&saddr, &len)) { + strncpy(dest, inet_ntoa(saddr.sin_addr), sizeof(dest)-1); + *(dest+sizeof(dest)-1) = 0; + } #if defined(COMPILE_DEBUG) - debug(2, "accepted %d=%s", nsock, peer); + debug(2, "accepted %d=%s wanting to go to %s", nsock, peer, NIL(dest)); #endif #if defined(HAVE_LIBWRAP) @@ -274,14 +303,18 @@ */ if (config_bool(NULL, "TCPWrapper", 0)) { struct request_info req; + char *wn; + + wn = config_str(NULL, "TCPWrapperName", misc_getprog()); + if( !(wn && *wn)) wn = "ftp-proxy"; /* fall back... */ - request_init(&req, RQ_DAEMON, misc_getprog(), + request_init(&req, RQ_DAEMON, wn, RQ_FILE, nsock, NULL); fromhost(&req); if (hosts_access(&req) == 0) { close(nsock); syslog_write(U_ERR, - "reject: '%s' (Wrap)", peer); + "%s reject: '%s' (Wrap)", wn, peer); return; } } @@ -1111,7 +1144,7 @@ break; case 'n': case 'N': - if (getdomainname(tmp, sizeof(tmp)) < 0) + if (getfqdomainname(tmp, sizeof(tmp)) < 0) strcpy(tmp, "[unknown network]"); break; case 't': @@ -1143,6 +1176,147 @@ /* ------------------------------------------------------------ ** ** +** Function......: socket_d_bind +** +** Parameters....: sock socket descriptor +** addr IP address we want to bind +** lrng Lower TCP port range limit +** urng Upper TCP port range limit +** incr use rand or increment mode +** +** Return........: bound port, 0 (INPORT_ANY) on failure +** +** Purpose.......: Binds a socket, taking care of a given +** port range using rand or incrementing +** the port number. +** Note: this function covers also dynamic +** ports assigning with a 0 range: +** lrng = urng = 0 ( = INPORT_ANY) +** +** ------------------------------------------------------------ */ +u_int16_t socket_d_bind(int sock, u_int32_t addr, + u_int16_t lrng, u_int16_t urng, + int incr) +{ + struct sockaddr_in saddr; + u_int16_t port = INPORT_ANY; + int retry= MAX_RETRIES, err = -1; + + /* Sanity check */ + if(sock < 0) + return INPORT_ANY; + + /* check port range */ + if( !(lrng<=urng)) { +#if defined(COMPILE_DEBUG) + debug(2, "socket_d_bind: invalid port range %d-%d", + lrng, urng); +#endif + return INPORT_ANY; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_addr.s_addr = htonl(addr); + saddr.sin_family = AF_INET; + +#if defined(COMPILE_DEBUG) + debug(4, "socket_d_bind using %s", incr ? "increment" : "random"); +#endif + + if(incr) { + for (port = lrng; err && (port <= urng); port++) { + + saddr.sin_port = htons(port); + + while(0 <= retry--) { + errno = 0; + err = bind(sock, (struct sockaddr *)&saddr, + sizeof(saddr)); +#if defined(COMPILE_DEBUG) + debug(2, "bind %s:%d result: %d, status: %s", + socket_addr2str(addr), + port, err, strerror(errno)); +#endif + if(0 == err) { + /* bind succeed */ +#if defined(COMPILE_DEBUG) + debug(2, "bind succeeded," + "port: %d, result: %d", + port, err); +#endif + retry = -1; break; + } else { + /* bind failed: fatal error? */ + if( !(EINTR == errno || + EAGAIN == errno || + EADDRINUSE == errno)) { +#if defined(COMPILE_DEBUG) + debug(4, "bind failed," + "result: %d, error %s", + err, strerror(errno)); +#endif + return INPORT_ANY; + } + } + } + } + } else { + int port_range = (urng - lrng) + 1; + + while(err && (0 < port_range--)) { + + port = misc_rand(lrng, urng); + saddr.sin_port = htons(port); + + while(0 <= retry--) { + err = bind(sock, (struct sockaddr *)&saddr, + sizeof(saddr)); +#if defined(COMPILE_DEBUG) + debug(2, "bind %s:%d, result: %d, status: %s", + socket_addr2str(addr), + port, err, strerror(errno)); +#endif + if(0 == err) { + /* bind succeed */ +#if defined(COMPILE_DEBUG) + debug(2, "bind succeeded, port: %d," + "result: %d", port, err); +#endif + retry = -1; break; + } else { + /* bind failed: fatal error? */ + if( !(EINTR == errno || + EAGAIN == errno || + EADDRINUSE == errno)) { +#if defined(COMPILE_DEBUG) + debug(2, "bind failed, " + "result: %d, error %s", + err, strerror(errno)); +#endif + return INPORT_ANY; + } + } + } + } + } + + if((0 == err) && + (INADDR_NONE != socket_sck2addr(sock, LOC_END, &port))) + { +#if defined(COMPILE_DEBUG) + debug(2, "bound socket to port %d", port); +#endif + return port; + } +#if defined(COMPILE_DEBUG) + debug(2, "bind error - port %d, IP %d", + port, socket_sck2addr(sock, LOC_END, &port)); +#endif + return INPORT_ANY; +} + +/* ------------------------------------------------------------ ** +** ** Function......: socket_d_listen ** ** Parameters....: addr IP address we want to bind @@ -1150,6 +1324,7 @@ ** urng Upper TCP port range limit ** phls Pointer where HLS will go ** ctyp Desired comms type identifier +** incr use rand or incremental bind ** ** Return........: Listening port (0=failure) ** @@ -1158,11 +1333,12 @@ ** ** ------------------------------------------------------------ */ -u_int16_t socket_d_listen(u_int32_t addr, u_int16_t lrng, - u_int16_t urng, HLS **phls, char *ctyp) +u_int16_t socket_d_listen(u_int32_t addr, + u_int16_t lrng, u_int16_t urng, + HLS **phls, char *ctyp, + int incr) { - struct sockaddr_in saddr; - int sock; + int sock; u_int16_t port; if (phls == NULL || ctyp == NULL) /* Sanity check */ @@ -1177,23 +1353,9 @@ } socket_opts(sock, SK_LISTEN); - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_addr.s_addr = htonl(addr); - saddr.sin_family = AF_INET; - - /* - ** Bind the socket, taking care of a given port range. - ** Note: this function covers also lrng = urng = 0 ... - */ - for (port = lrng; port <= urng; port++) { - saddr.sin_port = htons(port); - if (bind(sock, (struct sockaddr *) &saddr, - sizeof(saddr)) == 0) - break; - if (errno != EADDRINUSE) - port = urng + 1; - } - if (port > urng) { /* nothing found? */ + port = socket_d_bind(sock, addr, lrng, urng, incr); + if (INPORT_ANY == port) { + /* nothing found? */ close(sock); return 0; } @@ -1207,10 +1369,9 @@ (*phls)->sock = sock; (*phls)->ctyp = ctyp; - addr = socket_sck2addr(sock, LOC_END, &port); #if defined(COMPILE_DEBUG) debug(2, "listen: %s (fd=%d) %s:%d", (*phls)->ctyp, - (*phls)->sock, socket_addr2str(addr), (int) port); + (*phls)->sock, socket_addr2str(addr), (int)port); #endif return port; } @@ -1227,6 +1388,7 @@ ** urng Upper TCP port range limit ** phls Pointer where HLS will go ** ctyp Desired comms type identifier +** incr use rand or incremental bind ** ** Return........: Local end of connected port (0=failure) ** @@ -1236,61 +1398,114 @@ ** ------------------------------------------------------------ */ u_int16_t socket_d_connect(u_int32_t addr, u_int16_t port, - u_int32_t ladr, u_int16_t lrng, - u_int16_t urng, HLS **phls, char *ctyp) + u_int32_t ladr, + u_int16_t lrng, u_int16_t urng, + HLS **phls, char *ctyp, + int incr) { struct sockaddr_in saddr; - int sock; - u_int16_t lprt; + int sock = -1; /* mark socket invalid */ + int retry = MAX_RETRIES; + u_int16_t lprt = lrng; if (phls == NULL || ctyp == NULL) /* Sanity check */ misc_die(FL, "socket_d_connect: ?phls? ?ctyp?"); - /* - ** First of all, get a socket - */ - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - syslog_error("can't create %s socket", ctyp); - exit(EXIT_FAILURE); - } - socket_opts(sock, SK_DATA); + while(0 <= retry--) + { + /* + ** First of all, get a socket + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog_error("can't create %s socket", ctyp); + exit(EXIT_FAILURE); + } + socket_opts(sock, SK_DATA); - /* - ** Bind the socket, taking care of a given port range - */ - if (lrng > 0 && urng > 0) { + /* + ** check if we have to use a port range + */ + if( !(INPORT_ANY == lrng && INPORT_ANY == urng)) { + /* + ** Bind the socket, taking care of a given port range + */ + if(incr) { +#if defined(COMPILE_DEBUG) + debug(2, "%s: about to bind to %s:range(%d-%d)", + ctyp, socket_addr2str(ladr), + lprt, urng); +#endif + lprt = socket_d_bind(sock, ladr, + lprt, urng, incr); + } else { +#if defined(COMPILE_DEBUG) + debug(2, "%s: about to bind to %s:range(%d-%d)", + ctyp, socket_addr2str(ladr), + lrng, urng); +#endif + lprt = socket_d_bind(sock, ladr, + lrng, urng, incr); + } + if (INPORT_ANY == lprt) { + /* nothing found? */ + close(sock); + return 0; + } + } else lprt = INPORT_ANY; + + /* + ** Actually connect the socket + */ memset(&saddr, 0, sizeof(saddr)); - saddr.sin_addr.s_addr = htonl(ladr); + saddr.sin_addr.s_addr = htonl(addr); saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); - for (lprt = lrng; lprt <= urng; lprt++) { + if (connect(sock, (struct sockaddr *) &saddr, + sizeof(saddr)) < 0) + { #if defined(COMPILE_DEBUG) - debug(2, "try to con-bind %s to %s:%d", ctyp, - socket_addr2str(ladr), (int) lprt); + debug(2, "%s: connect failed with '%s'", + ctyp, strerror(errno)); #endif - saddr.sin_port = htons(lprt); - if (bind(sock, (struct sockaddr *) &saddr, - sizeof(saddr)) == 0) - break; - if (errno != EADDRINUSE) - lprt = urng + 1; - } - if (lprt > urng) { /* nothing found? */ close(sock); - return 0; - } + sock = -1; + /* check if is makes sense to retry? + ** perhaps we only need an other + ** local port (EADDRNOTAVAIL) for + ** this destination? + */ + if( !(EINTR == errno || + EAGAIN == errno || + EADDRINUSE == errno || + EADDRNOTAVAIL == errno)) + { + /* + ** an other (real) error ocurred + */ + return 0; + } else + if(incr && INPORT_ANY != lprt) { + /* + ** increment lower range if we use + ** increment mode and have a range + */ + if(lprt < urng) { + lprt++; /* incr lower range */ + } else { + /* + ** no more ports in range we can try + */ + return 0; + } + } + } else break; } /* - ** Actually connect the socket + ** check if we have a valid, connected socket */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_addr.s_addr = htonl(addr); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(port); - - if (connect(sock, (struct sockaddr *) &saddr, - sizeof(saddr)) < 0) { + if(-1 == sock) { close(sock); return 0; } @@ -1434,6 +1649,7 @@ ** port Pointer to port ** ** Return........: IP address in host order +** or INADDR_NONE on failure ** ** Purpose.......: Retrieve the IP address of a socket, ** for either the peer or the local end. @@ -1460,7 +1676,7 @@ } if (r < 0) { syslog_error("can't get %sname for socket %d", s, sock); - exit(EXIT_FAILURE); + return INADDR_NONE; } /* @@ -1475,6 +1691,174 @@ return (u_int32_t) ntohl(saddr.sin_addr.s_addr); } + +/* ------------------------------------------------------------ ** +** +** Function......: socket_chkladdr +** +** Parameters....: addr ip address to check +** +** Return........: 0 if not found, 1 if found, -1 on error +** +** Purpose.......: Check if IP address in addr is used on +** an local network interface. +** +** ------------------------------------------------------------ */ + +int socket_chkladdr(u_int32_t addr) +{ +/* #if defined(SIOCGIFCONF) */ +#define DEFAULT_IFNUM 512 + struct ifconf ifc; + int ifn = DEFAULT_IFNUM; + int i, sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if(-1 == sock) { +#if defined(COMPILE_DEBUG) + debug(2, "can not create socket: %s", NIL(strerror(errno))); +#endif + return -1; + } + +#if defined(SIOCGIFNUM) + if( ioctl(sock, SIOCGIFNUM, (char *) &ifn) < 0) { +#if defined(COMPILE_DEBUG) + debug(2, "ioctl SIOCGIFNUM failed: %s", NIL(strerror(errno))); +#endif + ifn = DEFAULT_IFNUM; /* ignore failure */ + } +#endif /* SIOCGIFNUM */ + + ifc.ifc_len = ifn * sizeof (struct ifreq); + ifc.ifc_buf = malloc(ifc.ifc_len); + if( !ifc.ifc_buf) { +#if defined(COMPILE_DEBUG) + debug(2, "malloc(ifc.ifc_len=%d) failed: %s", + ifc.ifc_len, NIL(strerror(errno))); +#endif + close(sock); + return -1; + } + memset(ifc.ifc_buf, 0, ifc.ifc_len); + + if( ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { +#if defined(COMPILE_DEBUG) + debug(2, "ioctl SIOCGIFCONF failed: %s", NIL(strerror(errno))); +#endif + free(ifc.ifc_buf); + close(sock); + return -1; + } + close(sock); + + for( i=0; iifr_addr; + + i += sizeof( *ifr); + + if(AF_INET != ifr->ifr_addr.sa_family + || INADDR_ANY == sa->sin_addr.s_addr + || INADDR_NONE == sa->sin_addr.s_addr) + continue; + +#if defined(COMPILE_DEBUG) + debug(4, "interface %s has ip-address: %s", + ifr->ifr_name, inet_ntoa(sa->sin_addr)); +#endif + + if( sa->sin_addr.s_addr == addr) { +#if defined(COMPILE_DEBUG) + debug(2, "found local ip addr: %s", + inet_ntoa(sa->sin_addr)); +#endif + free(ifc.ifc_buf); + return 1; + } + } + free(ifc.ifc_buf); + +/* #else */ /* SIOCGIFCONF */ +/* return -1; +#endif +*/ +#if defined(COMPILE_DEBUG) + debug(2, "requested ip addr is not a local one"); +#endif + return 0; +} + +/* ------------------------------------------------------------ ** +** +** Function......: getfqhostname +** +** Parameters....: fqhost buffer to store the host name +** n size of the buffer +** +** Return........: 0 on success, -1 on error +** +** Purpose.......: get the full qualified (resolved) +** host name of the current/local host +** +** ------------------------------------------------------------ */ + +int getfqhostname(char *fqhost, size_t n) +{ + char hname[MAXHOSTNAMELEN]; + struct hostent *hp; + + if( !(n > 0 && fqhost)) + return -1; + + memset(hname, 0, sizeof(hname)); + if( gethostname(hname, sizeof(hname)-1)) + return -1; + *(hname+sizeof(hname)-1) = 0; + + if( !(hp = gethostbyname(hname))) { + return -1; + } + strncpy(fqhost, hp->h_name, n-1); + *(fqhost+n-1) = 0; + + return 0; +} + +/* ------------------------------------------------------------ ** +** +** Function......: getfqdomainname +** +** Parameters....: fqhost buffer to store the domain name +** n size of the buffer +** +** Return........: 0 on success, -1 on error +** +** Purpose.......: get the full qualified (resolved) +** domain name of the current/local host +** +** ------------------------------------------------------------ */ + +int getfqdomainname(char *fqdomain, size_t n) +{ + char hname[MAXHOSTNAMELEN], *p; + + if( !(n > 0 && fqdomain)) + return -1; + + memset(hname, 0, sizeof(hname)); + if(getfqhostname(hname, sizeof(hname))) + return -1; + + p = strchr(hname, (int)'.'); + if(p && *(p+1)) { + strncpy(fqdomain, p+1, n-1); + *(fqdomain+n-1) = 0; + return 0; + } + + return -1; +} /* ------------------------------------------------------------ * $Log: com-socket.c,v $ --- common/com-socket.h +++ common/com-socket.h 2001/09/19 10:40:42 @@ -33,6 +33,23 @@ #define _COM_SOCKET_H_ /* ------------------------------------------------------------ */ +#if !defined(INADDR_NONE) +# if defined(INADDR_BROADCAST) +# define INADDR_NONE INADDR_BROADCAST +# else +# define INADDR_NONE ((uint32_t) 0xffffffffU) +# endif +#endif + +#if !defined(INPORT_ANY) +# define INPORT_ANY 0 +#endif + +#if !defined(MAXHOSTNAMELEN) +# define MAXHOSTNAMELEN 256 +#endif + +/* ------------------------------------------------------------ */ #define SK_LISTEN 1 /* Kind: listening socket */ #define SK_CONTROL 2 /* Kind: control connection */ @@ -43,6 +60,8 @@ #define PEER_LEN 32 /* Storage for dotted decimal */ +#define MAX_RETRIES 6 /* bind retries on EADDRINUSE */ + typedef void (*ACPT_CB)(int); /* Accept callback function */ @@ -92,17 +111,29 @@ char *socket_msgline(char *fmt); -u_int16_t socket_d_listen (u_int32_t addr, u_int16_t lrng, - u_int16_t urng, HLS **phls, char *ctyp); +u_int16_t socket_d_bind (int sock, u_int32_t addr, + u_int16_t lrng, u_int16_t urng, + int incr); + +u_int16_t socket_d_listen (u_int32_t addr, + u_int16_t lrng, u_int16_t urng, + HLS **phls, char *ctyp, + int incr); + u_int16_t socket_d_connect(u_int32_t addr, u_int16_t port, - u_int32_t ladr, u_int16_t lrng, - u_int16_t urng, HLS **phls, char *ctyp); + u_int32_t ladr, + u_int16_t lrng, u_int16_t urng, + HLS **phls, char *ctyp, + int incr); u_int32_t socket_str2addr(char *name, u_int32_t dflt); u_int16_t socket_str2port(char *name, u_int16_t dflt); char *socket_addr2str(u_int32_t addr); u_int32_t socket_sck2addr(int sock, int peer, u_int16_t *port); +int socket_chkladdr(u_int32_t addr); +int getfqhostname(char *fqhost, size_t n); +int getfqdomainname(char *fqdomain, size_t n); /* ------------------------------------------------------------ */ --- common/com-syslog.c +++ common/com-syslog.c 2001/09/19 10:40:42 @@ -71,6 +71,9 @@ # endif #endif +#include +#include + #include "com-debug.h" #include "com-misc.h" #include "com-syslog.h" @@ -186,12 +189,16 @@ ** So we do have a destination now ... */ if (*name == '/') { + char tmp_name[MAX_PATH_SIZE + 64]; /* ** Logging to a regular file */ - if (unlink(name) != 0 && errno != ENOENT) { - misc_die(FL, "can't remove logfile '%.*s'", + if(syslog_rename(tmp_name, name, sizeof(tmp_name))<0) + { + if (unlink(name) != 0 && errno != ENOENT) { + misc_die(FL, "can't remove logfile '%.*s'", MAX_PATH_SIZE, name); + } } if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0640)) < 0) { @@ -404,6 +411,53 @@ /* ------------------------------------------------------------ ** ** +** Function......: syslog_rename +** +** Parameters....: new_name, log_name, len +** +** Return........: -1 on error, 0 on success, +** 1 if log_name does not exists +** +** Purpose.......: if file specified as log_name exists, +** rename it to new_name with a maximal +** length given in len. +** +** ------------------------------------------------------------ */ + +int syslog_rename(char *new_name, char *log_name, size_t len) +{ + time_t now; + struct tm *t; + struct stat st; + + if( !(len > 0 && new_name && log_name)) + return -1; + + time(&now); + t = localtime(&now); + sprintf(new_name, "%.*s.%d%02d%02d-%02d%02d%02d", + (int)len, log_name, t->tm_year + 1900, + t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + + if( stat(log_name, &st)) + return 1; + + if( !stat(new_name, &st)) { + if( !(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) { + return -1; + } + unlink(new_name); + } + + if (rename(log_name, new_name)) { + return -1; + } + return 0; +} + +/* ------------------------------------------------------------ ** +** ** Function......: syslog_rotate ** ** Parameters....: (none) @@ -418,8 +472,6 @@ { char tmp_name[MAX_PATH_SIZE + 64]; int fd; - time_t now; - struct tm *t; if (log_file == NULL) /* Only useful if logging to file */ return; @@ -431,16 +483,9 @@ fclose(log_file); log_file = NULL; - time(&now); - t = localtime(&now); - sprintf(tmp_name, "%s.%d%02d%02d-%02d%02d%02d", - log_name, t->tm_year + 1900, - t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); - if (rename(log_name, tmp_name)) { - misc_die(FL, "can't rename '%.*s' to '%.*s'", - MAX_PATH_SIZE, log_name, - MAX_PATH_SIZE, tmp_name); + if(syslog_rename(tmp_name, log_name, sizeof(tmp_name))<0) { + misc_die(FL, "can't rotate logfile '%.*s'", + MAX_PATH_SIZE, log_name); } if ((fd = open(log_name, O_RDWR | O_CREAT | O_EXCL, 0640)) < 0) { --- common/com-syslog.h +++ common/com-syslog.h 2001/09/19 10:40:42 @@ -52,6 +52,7 @@ void syslog_open (char *name); void syslog_write (int level, char *fmt, ...); void syslog_error (char *fmt, ...); +int syslog_rename(char *new_name, char *log_name, size_t len); void syslog_rotate(void); void syslog_close (void); --- config.h.in +++ config.h.in 2001/09/19 10:40:42 @@ -131,9 +131,24 @@ /* Define if you have the header file. */ #undef HAVE_MEMORY_H +/* Define if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IN_SYSTM_H + +/* Define if you have the header file. */ +#undef HAVE_NETINET_IP_H + /* Define if you have the header file. */ #undef HAVE_SYS_FCNTL_H +/* Define if you have the header file. */ +#undef HAVE_SYS_FILIO_H + /* Define if you have the header file. */ #undef HAVE_SYS_SELECT_H @@ -148,6 +163,9 @@ /* Define if you have the header file. */ #undef HAVE_UNISTD_H + +/* Define if you have the compat library (-lcompat). */ +#undef HAVE_LIBCOMPAT /* Define if you have the nsl library (-lnsl). */ #undef HAVE_LIBNSL --- configure.in +++ configure.in 2001/09/19 12:19:20 @@ -134,11 +134,15 @@ AC_C_INLINE AC_C_CONST -AC_CHECK_PROG(CTAGS, ctags, ctags) +TAGS="" +CTAGS="" +CTAG_OPTS="" +AC_CHECK_PROGS(CTAGS, gnuctags ctags) if test -n "$CTAGS"; then TAGS="tags" -else - TAGS="" + if test "$CTAGS" = gnutags ; then + CTAG_OPTS="-w" + fi fi @@ -162,11 +166,9 @@ AC_HEADER_TIME AC_CHECK_HEADERS(sys/time.h) -AC_CHECK_HEADERS(sys/select.h) -AC_CHECK_HEADERS(sys/sockio.h) - -AC_CHECK_HEADERS(fcntl.h) -AC_CHECK_HEADERS(sys/fcntl.h) +AC_CHECK_HEADERS(sys/select.h sys/sockio.h fcntl.h sys/fcntl.h) +AC_CHECK_HEADERS(net/if.h netinet/ip.h netinet/in.h sys/conf.h) +AC_CHECK_HEADERS(sys/filio.h netinet/in_systm.h stropts.h) AC_HEADER_SYS_WAIT @@ -180,6 +182,7 @@ AC_CHECK_FUNCS(setsid) +dnl AC_CHECK_FUNCS(getdomainname) ############################################################ # check for types to be defined @@ -199,9 +202,10 @@ # check for libraries to scan ############################################################ -AC_CHECK_LIB(nsl, gethostent) +AC_CHECK_LIB(compat, main) AC_CHECK_LIB(socket, connect) - +AC_CHECK_LIB(nsl, gethostent,,[ + AC_CHECK_LIB(resolv, gethostbyname)]) ############################################################ # check whether to enable /proc modules @@ -302,11 +306,11 @@ yes) AC_MSG_RESULT(yes) AC_CHECK_FUNC(regcomp, - AC_DEFINE(HAVE_REGEX), - AC_CHECK_LIB(regex, regcomp, [ - LIB_REGEX="-lregex" - AC_DEFINE(HAVE_REGEX) ] - ) + AC_DEFINE(HAVE_REGEX) + ) + AC_CHECK_LIB(regex, regcomp, [ + LIB_REGEX="-lregex" + AC_DEFINE(HAVE_REGEX) ] ) ;; *) @@ -385,15 +389,21 @@ ;; yes) AC_MSG_RESULT(yes) - AC_CHECK_LIB(ldap, ldap_set_option, [ + AC_CHECK_LIB(ldap, ldap_get_lderrno, [ LIB_LDAP="-lldap" AC_DEFINE(HAVE_LIBLDAP) AC_DEFINE(HAVE_LDAP_NETSCAPE) ], - AC_CHECK_LIB(lber, ber_init, [ - LIB_LDAP="-lldap -llber" + AC_CHECK_LIB(ldap, ber_init, [ + LIB_LDAP="-lldap" AC_DEFINE(HAVE_LIBLDAP) - AC_DEFINE(HAVE_LDAP_UMICH) ] + AC_DEFINE(HAVE_LDAP_UMICH) + ], + AC_CHECK_LIB(lber, ber_init, [ + LIB_LDAP="-lldap -llber" + AC_DEFINE(HAVE_LIBLDAP) + AC_DEFINE(HAVE_LDAP_UMICH) ] + ) ) ) ;; @@ -435,6 +445,8 @@ AC_SUBST(CFLAGS) AC_SUBST(LDFLAGS) AC_SUBST(TAGS) +AC_SUBST(CTAGS) +AC_SUBST(CTAG_OPTS) AC_SUBST(LIB_WRAP) AC_SUBST(LIB_LDAP) AC_SUBST(LIB_REGEX) --- doc/src/ftp-proxy.sgml +++ doc/src/ftp-proxy.sgml 2001/09/19 10:40:42 @@ -656,7 +656,7 @@ ), and

-Marius Tomaschewski (test environment, +Marius Tomaschewski (test environment, programming, ). --- ftp-proxy/Chroot-Mini-Howto.txt +++ ftp-proxy/Chroot-Mini-Howto.txt 2001/09/19 11:50:43 @@ -0,0 +1,103 @@ + + SuSE FTP-Proxy chroot setup + + Marius Tomaschewski + v0.1, 30 November 2000 + + + How to start the SuSE FTP-Proxy in a chroot Environment? + ======================================================== + + This version provides a sample LSB run-level script, that + allows you to start the proxy in daemon mode in a chroot + enviroment _or_ in "normal" standalone / daemon mode. + See rc.script.ftp-proxy and rc.config.ftp-proxy files. + It also creates the complete chroot environment for you. + + The run-level script is very blown and tries to handle + some different settings, like LogDestination, PidFile, + TCPWrapper, LDAPServer, Group, Messages. + + The script does NOT use the built-in chroot setting of + the ftp-proxy to chroot (on client connect) into the + directory given with ServerRoot in the configuration. + + It starts the daemon via compartment (by Marc Heuse). + The benefit of compartment usage is that compartment + cleanups the enviroment, does a chroot, changes the + group, sets kernel capabilities and _after_ all this + steps are done, executes the daemon inside of this + environment. + The daemon runs with UID 0 (as root) in the chroot, + but without any root permissions, except of bind to + a port lower than 1024 (server port) and access files + owned by himself (root). + + IMHO it is a safer chroot than without compartment; + if you do not like it, write your own script :-) + + The script uses the configuration file of the + proxy (default is /etc/proxy-suite/ftp-proxy.conf) + to setup the chroot environment, so all you + have to do, is to make some settings in this file. + + The config steps in /etc/proxy-suite/ftp-proxy.conf: + + * Set ``ServerType to创 'standalone' (otherwise the + script refuses to start). + + * Set a directory with ``ServerRoot创, i.e. the + default directory '/var/ftp-proxy/rundir'. + + * Set a ``Group创, the proxy should run with (i.e. + 'ftpproxy'). Do NOT try to set a ``User创 - it + will _never_ be used. + + * Set a ``LogDestination创. The proxy supports logging + to a pipe - the script doesn't support it, so you + have to use a file (i.e. '/var/log/ftp-proxy.log') + or a syslog facility (i.e. 'daemon'). + If you are using a file, allways set an absolute name; + both, the script and the ftp-proxy need this. + If you are using a syslog facility, you have to say + syslogd to create a log-socket inside of the chroot + environment - simply add it to the SYSLOGD_PARMS + variable in /etc/rc.config, i.e.: + SYSLOGD_PARMS="-a /var/ftp-proxy/rundir/dev/log" + + * Set a ``DestinationAddress创 _OR_ enable the new + transparent proxying feature with ``AllowTransProxy创 + (see TRANSPARENT_PROXY.txt). + + * Make other settings you need/like... + + That's all - now start the script with: + + /sbin/rcftp-proxy chroot + + and it will create a chroot enviroment for you with + all needed files (I hope ;-) inside. Now, you can + start the proxy with: + + /sbin/rcftp-proxy start + + and TEST if it does what you want - if not, read + the documentation and double-check your settings. + + You can take a look into the configuration file inside + of the chroot environment (with the above settings it + is /var/ftp-proxy/rundir/etc/proxy-suite/ftp-proxy.conf). + Some of your settings are not there (Group, ServerRoot); + this happens because we are using compartment and not + the built-in chroot of the ftp-proxy (see above). + + Please contact proxy-suite@suse.com if you have any ideas, + additions or corrections to this text, the script or the + ftp-proxy. Please do not ask me for technical support; + you can purchase Unix consulting (solutions@suse.de) and + support (bsupport@suse.de). + + +Have a lot fun, + Your SuSE Team. + --- ftp-proxy/Makefile.in +++ ftp-proxy/Makefile.in 2001/09/19 10:40:42 @@ -44,7 +44,7 @@ INSTALL= @INSTALL@ CC= @CC@ -CFLAGS= @CFLAGS@ +CFLAGS= @CFLAGS@ -DETC_DIR=\"$(ETC_DIR)\" LDFLAGS= @LDFLAGS@ LIBS= @LIB_WRAP@ @LIB_LDAP@ @LIB_REGEX@ @LIBS@ @@ -61,6 +61,8 @@ FTP_MAN8= ftp-proxy.8 TAGS= @TAGS@ +CTAGS= @CTAGS@ +CTAG_OPTS= @CTAG_OPTS@ FTP_SRCS= ftp-client.c \ ftp-cmds.c \ @@ -91,7 +93,7 @@ all: $(TAGS) progs tags: $(FTP_SRCS) $(FTP_HDRS) - ctags -w $(FTP_SRCS) $(FTP_HDRS) ../common/*.[ch] + $(CTAGS) $(CTAG_OPTS) $(FTP_SRCS) $(FTP_HDRS) ../common/*.[ch] @echo "" progs: ftp-proxy $(PROC_FTP) --- ftp-proxy/TransProxy-Mini-Howto.txt +++ ftp-proxy/TransProxy-Mini-Howto.txt 2001/09/19 10:40:42 @@ -0,0 +1,66 @@ + +Warning: The implementation of transparent proxying support +is tested only with the transparent proxy feature of the +Linux 2.2.x kernels and ipchains. + + Feel free to test it on other platforms! + + +How it works? + +Its very simple: the ip filter of you kernel redirects all +packages to the ftp port (21) in "external networks" to the +proxy and the proxy connects the server you wanted to go +using informations from the ip package of your request. + + +First, you want to enable the transparent proxy feature in +your kernel and setup redirection rules: + +Here is a example network configuration: + + internal network - 192.168.0.0/24 (and others) + | + | + | eth0: 192.168.1.1 + Firewall/Gateway + Proxy + | eth1: 200.200.200.1 + | + | + I N T E R N E T + +You want following (relevant) rules in your IP filter, i.e.: + +# deny all FTP requests to the proxy from internet +ipchains -I input -i eth1 -d 192.168.1.1 21 -p tcp -j DENY -l + +# redirect all FTP requests from internal network to the proxy: +ipchains -I input -i eth0 -s 0/0 -d ! 192.168.1.1 21 -p tcp -j REDIRECT 21 + + +Second, you want to enable the AllowTransProxy flag in the +ftp-proxy.conf(5) file and start the proxy. + +This is a MINIMALISTIC example you may start with: + +# grep -v ^# /etc/proxy-suite/ftp-proxy.conf | grep -v ^$ +[-Global-] +ServerType standalone +LogDestination daemon +DestinationTransferMode passive +PortResetsPasv yes +Listen 192.168.1.1 +# may be needed in NAT'ed/Masqueraded environments +#TranslatedAddress 200.200.200.1 +UseMagicChar % +AllowMagicUser yes +AllowTransProxy yes + + +Take a look on your system messages if and how it works! +And sure, feel free to set up a chroot environment, etc +(see sample run level script in rc.script.ftp-proxy). + +Have a lot fun, + Your SuSE Team. + --- ftp-proxy/ftp-client.c +++ ftp-proxy/ftp-client.c 2001/09/19 10:40:42 @@ -445,7 +445,10 @@ ctx.cli_ctrl->peer); socket_printf(ctx.cli_ctrl, "%c%c%c", IAC, DONT, arg[2]); - memmove(arg, arg + 3, strlen(arg) - 2); + if(arg[2]) + memmove(arg, arg + 3, strlen(arg) - 2); + else + memmove(arg, arg + 1, strlen(arg)); break; case DO: @@ -458,7 +461,10 @@ ctx.cli_ctrl->peer); socket_printf(ctx.cli_ctrl, "%c%c%c", IAC, WONT, arg[2]); - memmove(arg, arg + 3, strlen(arg) - 2); + if(arg[2]) + memmove(arg, arg + 3, strlen(arg) - 2); + else + memmove(arg, arg + 1, strlen(arg)); break; case IAC: @@ -470,11 +476,11 @@ syslog_write(U_INF, "IAC-%s from %s", (c == IP) ? "IP" : "DM", ctx.cli_ctrl->peer); - memmove(str, arg + 2, strlen(arg) + 1); + memmove(arg, arg + 2, strlen(arg) - 1); break; default: - memmove(arg, arg + 2, strlen(arg) + 1); + memmove(arg, arg + 1, strlen(arg)); } } @@ -515,7 +521,7 @@ cmd->legal = 1; /* Need this one! */ if (strcasecmp(str, cmd->name) != 0) continue; - if (cmd->legal == 0) { + if ((cmd->legal == 0) && strcasecmp("QUIT", cmd->name)) { client_respond(502, NULL, "'%.32s': " "command not implemented", str); syslog_write(U_WRN, @@ -729,6 +735,7 @@ int h1, h2, h3, h4, p1, p2; u_int32_t addr, ladr; u_int16_t port; + int incr; if (arg == NULL) /* Basic sanity check */ return; @@ -752,11 +759,18 @@ port = (u_int16_t) ((p1 << 8) + p2); /* + ** should we bind a rand(port-range) or increment? + */ + incr = !config_bool(NULL,"SockBindRand", 0); + + /* ** Open a connection to the server at the given port */ ladr = socket_sck2addr(ctx.srv_ctrl->sock, LOC_END, NULL); if (socket_d_connect(addr, port, ladr, ctx.srv_lrng, - ctx.srv_urng, &(ctx.srv_data), "Srv-Data") == 0) { + ctx.srv_urng, &(ctx.srv_data), + "Srv-Data", incr) == 0) + { syslog_error("can't connect Srv-Data for %s", ctx.cli_ctrl->peer); client_respond(425, NULL, "Can't open data connection"); @@ -789,17 +803,35 @@ static void client_xfer_fireup(void) { - u_int32_t ladr; + u_int32_t ladr = INADDR_ANY; + int incr; + + /* + ** should we bind a rand(port-range) or increment? + */ + incr = !config_bool(NULL,"SockBindRand", 0); /* ** If appropriate, connect to the client's data port */ if (ctx.cli_mode == MOD_ACT_FTP) { - ladr = socket_sck2addr(ctx.cli_ctrl->sock, + /* + ** TransProxy mode: check if we can use our real + ** ip instead of the server's one as our local ip, + ** we pre-bind the socket/ports to before connect. + */ + if(config_bool(NULL, "AllowTransProxy", 0)) { + ladr = config_addr(NULL, "Listen", + (u_int32_t)INADDR_ANY); + } + if(INADDR_ANY == ladr) { + ladr = socket_sck2addr(ctx.cli_ctrl->sock, LOC_END, NULL); + } if (socket_d_connect(ctx.cli_addr, ctx.cli_port, ladr, ctx.act_lrng, ctx.act_urng, - &(ctx.cli_data), "Cli-Data") == 0) { + &(ctx.cli_data), "Cli-Data", incr) == 0) + { syslog_error("can't connect Cli-Data for %s", ctx.cli_ctrl->peer); client_respond(425, NULL, --- ftp-proxy/ftp-cmds.c +++ ftp-proxy/ftp-cmds.c 2001/09/19 10:40:42 @@ -247,11 +247,11 @@ ** Scan the allow list and enable accordingly */ for (p = allow; *p != '\0'; ) { - while (*p != '\0' && isalpha(*p) == 0) + while (*p != '\0' && isalpha((int)*p) == 0) p++; if (*p == '\0') break; - for (q = p, i = 0; isalpha(*q); q++, i++) + for (q = p, i = 0; isalpha((int)*q); q++, i++) ; for (cmd = cmdlist; cmd->name; cmd++) { if (cmd->len != i) @@ -340,11 +340,12 @@ static void cmds_user(CONTEXT *ctx, char *arg) { - char *p, *q; - CMD *cmd; - int sock; struct sockaddr_in saddr; - u_int16_t lprt; + int sock, len; + int incr, retry; + u_int16_t lprt, lowrng, bind_res; + char *p, *q; + CMD *cmd; if (ctx == NULL) /* Basic sanity check */ misc_die(FL, "cmds_user: ?ctx?"); @@ -364,6 +365,7 @@ */ client_reinit(); +#if defined(HAVE_REGEX) /* ** Check for a RegEx constraint on the USER command */ @@ -385,15 +387,69 @@ } break; } +#endif /* - ** Check for permission and existence of "magic" - ** destination address and port information + ** Check for permission and existence of "transparent proxy" + ** magic destination address and port from the client socket */ ctx->magic_addr = 0; ctx->magic_port = 0; - if (config_bool(NULL, "AllowMagicUser", 0) != 0 && - (p = strchr(arg, '@')) != NULL) { + len = sizeof(saddr); + memset(&saddr, 0, len); + if (config_bool(NULL, "AllowTransProxy", 0) && + !getsockname(ctx->cli_ctrl->sock, (struct sockaddr *)&saddr, &len)) + { + char dest[PEER_LEN] = {0}; + u_int32_t ip = saddr.sin_addr.s_addr; + int rc; + + strncpy(dest, inet_ntoa(saddr.sin_addr), sizeof(dest)-1); + *(dest+sizeof(dest)-1) = 0; + + /* syslog_write(U_INF, + "checking transparent proxy dest: %s", dest); */ + + if(ip == INADDR_ANY || ip == INADDR_NONE) + { + syslog_write(U_ERR, + "invalid transparent proxy dest: %s", dest); + client_respond(501, NULL, + "Invalid transparent proxy destination: %s", + dest); + return; + } + + rc = socket_chkladdr(ip); + switch( rc) { + case 0: + ctx->magic_addr = ntohl(ip); + ctx->magic_port = ntohs(saddr.sin_port); + syslog_write(U_INF, + "transparent proxy request to %s:%d from %s", + dest, ctx->magic_port, ctx->cli_ctrl->peer); + break; + case -1: + syslog_write(U_ERR, + "check of transparent proxy dest %s failed", + dest); + break; + default: + syslog_write(U_WRN, + "requested transparent proxy dest %s is local", + dest); + break; + } + } + + + /* + ** Check for permission and existence of "magic" + ** destination address and port information + */ + if(config_bool(NULL, "AllowMagicUser", 0) != 0 && + (p = strchr(arg, *config_str(NULL, "UseMagicChar", "@"))) != NULL) + { *p++ = '\0'; if ((q = strchr(p, ':')) != NULL) { *q++ = '\0'; @@ -415,81 +471,168 @@ /* ** Retrieve the relevant user information */ - ctx->username = misc_strdup(FL, arg); if (ctx->magic_addr != INADDR_ANY) { syslog_write(U_INF, "'USER %s' dest %s:%d from %s", arg, socket_addr2str(ctx->magic_addr), (int) ctx->magic_port, ctx->cli_ctrl->peer); + } else if(config_str(NULL, "DestinationAddress", NULL) == NULL) { + syslog_write(U_ERR, "Unknown destination address"); + client_respond(501, NULL,"Unknown destination address"); + return; } else { syslog_write(U_INF, "'USER %s' from %s", arg, ctx->cli_ctrl->peer); } + ctx->username = misc_strdup(FL, arg); ldap_setup_user(ctx); /* - ** Forward connection to destination + ** should we bind a rand(port-range) or increment? */ - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - syslog_error("can't create Srv-Ctrl for %s", - ctx->cli_ctrl->peer); - exit(EXIT_FAILURE); - } - socket_opts(sock, SK_CONTROL); + incr = !config_bool(NULL,"SockBindRand", 0); /* - ** Bind the socket, taking care of a given port range + ** mark socket invalid */ - if (ctx->srv_lrng > 0 && ctx->srv_urng > 0) { + sock = -1; + + /* + ** Forward connection to destination + */ + retry = MAX_RETRIES; + lprt = ctx->srv_lrng; + while(0 <= retry--) { + /* + ** First of all, get a socket + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog_error("can't create Srv-Ctrl socket for %s", + ctx->cli_ctrl->peer); + exit(EXIT_FAILURE); + } + socket_opts(sock, SK_CONTROL); + + /* + ** check if we have to take care to a port range + */ + if( !(INPORT_ANY == ctx->srv_lrng && + INPORT_ANY == ctx->srv_urng)) + { + u_int32_t ladr = INADDR_ANY; + + /* + ** bind the socket, taking care of a given port range + */ + if(incr) { + lowrng = lprt; +#if defined(COMPILE_DEBUG) + debug(2, "Srv-Ctrl: " + "about to bind to %s:range(%d-%d)", + socket_addr2str(ladr), + lowrng, ctx->srv_urng); +#endif + bind_res = socket_d_bind(sock, ladr, + lowrng, ctx->srv_urng, incr); + } else { + lowrng = ctx->srv_lrng; +#if defined(COMPILE_DEBUG) + debug(2, "Srv-Ctrl: " + "about to bind to %s:range(%d-%d)", + socket_addr2str(ladr), + lowrng, ctx->srv_urng); +#endif + bind_res = socket_d_bind(sock, ladr, + lowrng, ctx->srv_urng, incr); + } + if (INPORT_ANY == bind_res) { + /* nothing found? */ + close(sock); + syslog_error("Srv-Ctrl: can't bind to" + " %s:%d for %s", + socket_addr2str(ladr), + (int)lprt, ctx->cli_ctrl->peer); + exit(EXIT_FAILURE); + } else { + lprt = bind_res; + } + } else lprt = INPORT_ANY; + + /* + ** Okay, now try the actual connect to the server + */ memset(&saddr, 0, sizeof(saddr)); - saddr.sin_addr.s_addr = INADDR_ANY; + saddr.sin_addr.s_addr = htonl(ctx->srv_addr); saddr.sin_family = AF_INET; + saddr.sin_port = htons(ctx->srv_port); - for (lprt = ctx->srv_lrng; - lprt <= ctx->srv_urng; lprt++) { + if (connect(sock, (struct sockaddr *)&saddr, + sizeof(saddr)) < 0) + { #if defined(COMPILE_DEBUG) - debug(2, "try to con-bind Srv-Ctrl to 0:%d", - (int) lprt); + debug(2, "Srv-Ctrl: connect failed with '%s'", + strerror(errno)); #endif - saddr.sin_port = htons(lprt); - if (bind(sock, (struct sockaddr *) &saddr, - sizeof(saddr)) == 0) - break; - if (errno != EADDRINUSE) - lprt = ctx->srv_urng + 1; - } - if (lprt > ctx->srv_urng) { /* nothing found? */ close(sock); - syslog_error("can't bind Srv-Ctrl " - "to 0:%d-%d for %s", - (int) ctx->srv_lrng, - (int) ctx->srv_urng, - ctx->cli_ctrl->peer); - exit(EXIT_FAILURE); - } + sock = -1; + /* check if is makes sense to retry? + ** perhaps we only need an other + ** local port (EADDRNOTAVAIL) for + ** this destination? + */ + if( !(EINTR == errno || + EAGAIN == errno || + EADDRINUSE == errno || + EADDRNOTAVAIL == errno)) + { + /* + ** an other (real) error ocurred + */ + syslog_error("Srv-Ctrl: " + "can't connect %s:%d for %s", + socket_addr2str(ctx->srv_addr), + (int) ctx->srv_port, + ctx->cli_ctrl->peer); + exit(EXIT_FAILURE); + } else + if(incr && INPORT_ANY != lprt) { + /* increment lower range if we use + ** increment mode and have a range + */ + if(lprt < ctx->srv_urng) { + lprt++; + } else { + /* + ** no more ports in range we can try + */ + syslog_error("Srv-Ctrl: " + "can't connect %s:%d for %s", + socket_addr2str(ctx->srv_addr), + (int) ctx->srv_port, + ctx->cli_ctrl->peer); + exit(EXIT_FAILURE); + } + } + } else break; } /* - ** Okay, now try the actual connect to the server + ** check if we have a valid, connected socket */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_addr.s_addr = htonl(ctx->srv_addr); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(ctx->srv_port); - - if (connect(sock, (struct sockaddr *) &saddr, - sizeof(saddr)) < 0) { - syslog_error("can't connect %s:%d for %s", - socket_addr2str(ctx->srv_addr), - (int) ctx->srv_port, ctx->cli_ctrl->peer); + if(-1 == sock) { + syslog_error("Srv-Ctrl: can't connect %s:%d for %s", + socket_addr2str(ctx->srv_addr), + (int) ctx->srv_port, + ctx->cli_ctrl->peer); exit(EXIT_FAILURE); } + if ((ctx->srv_ctrl = socket_init(sock)) == NULL) misc_die(FL, "cmds_user: ?srv_ctrl?"); ctx->srv_ctrl->ctyp = "Srv-Ctrl"; #if defined(COMPILE_DEBUG) - debug(2, "Srv-Ctrl is %s:%d", + debug(2, "Srv-Ctrl is %s:%d", ctx->srv_ctrl->peer, (int) ctx->srv_port); #endif @@ -711,10 +854,11 @@ static void cmds_pasv(CONTEXT *ctx, char *arg) { - u_int32_t addr; + u_int32_t addr = INADDR_ANY; u_int16_t port; char str[1024], *p, *q; FILE *fp; + int incr; if (ctx == NULL) /* Basic sanity check */ misc_die(FL, "cmds_pasv: ?ctx?"); @@ -731,11 +875,26 @@ } /* + ** should we bind a rand(port-range) or increment? + */ + incr = !config_bool(NULL,"SockBindRand", 0); + + /* ** Open a socket that is good for listening + ** + ** TransProxy mode: check if we can use our real + ** ip instead of the server's one as our local ip, + ** we bind the socket/ports to. */ - addr = socket_sck2addr(ctx->cli_ctrl->sock, LOC_END, NULL); + if(config_bool(NULL, "AllowTransProxy", 0)) { + addr = config_addr(NULL, "Listen", (u_int32_t)INADDR_ANY); + } + if(INADDR_ANY == addr) { + addr = socket_sck2addr(ctx->cli_ctrl->sock, LOC_END, NULL); + } if ((port = socket_d_listen(addr, ctx->pas_lrng, ctx->pas_urng, - &(ctx->cli_data), "Cli-Data")) == 0) { + &(ctx->cli_data), "Cli-Data", incr)) == 0) + { syslog_error("can't bind Cli-Data to %s:%d-%d for %s", socket_addr2str(addr), (int) ctx->pas_lrng, (int) ctx->pas_urng, ctx->cli_ctrl->peer); @@ -806,7 +965,7 @@ static void cmds_xfer(CONTEXT *ctx, char *arg) { - int mode; + int mode = MOD_ACT_FTP; char *cmd; u_int32_t addr; u_int16_t port; @@ -852,11 +1011,17 @@ ** In active mode we listen and the server connects */ if (mode == MOD_ACT_FTP) { + /* + ** should we bind a rand(port-range) or increment? + */ + int incr = !config_bool(NULL,"SockBindRand", 0); + addr = socket_sck2addr(ctx->srv_ctrl->sock, LOC_END, NULL); + if ((port = socket_d_listen(addr, ctx->srv_lrng, ctx->srv_urng, &(ctx->srv_data), - "Srv-Data")) == 0) { + "Srv-Data", incr)) == 0) { syslog_error("can't bind Srv-Data to " "%s:%d-%d for %s", socket_addr2str(addr), @@ -1087,7 +1252,7 @@ str[i++] = *ptr; continue; } - if (isxdigit(ptr[1]) && isxdigit(ptr[2])) { + if (isxdigit((int)ptr[1]) && isxdigit((int)ptr[2])) { sprintf(tmp, "%.2s", ptr + 1); sscanf(tmp, "%x", &c); str[i++] = (char) c; --- ftp-proxy/ftp-daemon.c +++ ftp-proxy/ftp-daemon.c 2001/09/19 10:40:42 @@ -96,7 +96,7 @@ /* ------------------------------------------------------------ */ #define MAX_CLIENTS 512 /* Max. concurrent user limit */ -#define MAX_RETRIES 6 /* Wait up to 30sec for listen */ +#define LISTEN_WAIT 30 /* Wait up to 30sec for listen */ #define FORK_INTERVAL 60 /* Interval for ForkLimit */ #define MAX_FORKS 40 /* Default fork-resource-limit */ @@ -115,8 +115,8 @@ /* ------------------------------------------------------------ */ -static int initflag = 0; /* Have we been initialized? */ - +static int initflag = 0; /* Have we been initialized? */ +static pid_t daemon_pid = 0; /* Daemon PID for cleanups, ... */ static time_t last_slice = 0; /* Last time slice with clients */ static int last_count = 0; /* Clients in last_slice */ @@ -211,6 +211,7 @@ break; default: /******** parent ********/ + daemon_pid = getpid(); #if defined(COMPILE_DEBUG) debug_forget(); #endif @@ -248,7 +249,7 @@ for (i = 0; i < MAX_RETRIES; i++) { if (socket_listen(laddr, lport, daemon_accept) == 0) break; - sleep(5); + sleep(LISTEN_WAIT); } if (i >= MAX_RETRIES) { syslog_error("can't bind daemon to %d", (int) lport); @@ -261,14 +262,14 @@ signal(SIGCHLD, daemon_signal); /* - ** Change our user- and group-id if requested + ** Create a PID-File if requested */ - misc_uidgid(CONFIG_UID, CONFIG_GID); + misc_pidfile(config_str(NULL, "PidFile", NULL)); /* - ** Create a PID-File if requested + ** Change our user- and group-id if requested */ - misc_pidfile(config_str(NULL, "PidFile", NULL)); + misc_uidgid(CONFIG_UID, CONFIG_GID); } @@ -326,6 +327,17 @@ else if (cnt > MAX_CLIENTS) cnt = MAX_CLIENTS; for (i = 0, clp = clients; i < cnt; i++, clp++) { + /* + ** santoniu@libertysurf.fr: + ** Verifying if the child is alive or not. + */ + if ((clp->pid != (pid_t) 0) && (kill(clp->pid, 0)!=0) ) { + syslog_write(T_WRN, + "child with PID %d went away (removing it)", + (pid_t)clp->pid); + clp->pid = 0; + break; + } if (clp->pid == (pid_t) 0) break; } @@ -365,7 +377,6 @@ clp->pid = (pid_t) 0; if (errno != EAGAIN) { syslog_error("can't fork client"); - exit(EXIT_FAILURE); } close(sock); syslog_write(T_WRN, "can't fork client now"); @@ -430,6 +441,7 @@ int i; CLIENT *clp; + if(daemon_pid == getpid()) /* clean up our childs list */ for (i = 0, clp = clients; i < MAX_CLIENTS; i++, clp++) { if (clp->pid == (pid_t) 0) continue; --- ftp-proxy/ftp-ldap.c +++ ftp-proxy/ftp-ldap.c 2001/09/19 10:40:42 @@ -88,7 +88,16 @@ #if defined(HAVE_LIBLDAP) # if defined(HAVE_LDAP_UMICH) -# define GET_LDERROR(ld) (ld)->ld_errno +# if defined __sun__ + /* + * there is only a forward definition of the LDAP + * connection handle struct in ldap.h on Solaris7, + * so we have no access to ld_errno. + */ +# define GET_LDERROR(ld) LDAP_OTHER +# else +# define GET_LDERROR(ld) (ld)->ld_errno +# endif # else # define GET_LDERROR(ld) ldap_get_lderrno((ld), NULL, NULL) # endif @@ -222,42 +231,48 @@ /* ** Evaluate the port ranges */ - l = config_port(ctx->username, "DestinationMinPort", 0); - u = config_port(ctx->username, "DestinationMaxPort", 0); + l = config_port(ctx->username, "DestinationMinPort", INPORT_ANY); + u = config_port(ctx->username, "DestinationMaxPort", INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->srv_lrng = l; ctx->srv_urng = u; } else { - ctx->srv_lrng = 0; - ctx->srv_urng = 0; + ctx->srv_lrng = INPORT_ANY; + ctx->srv_urng = INPORT_ANY; } #if defined(COMPILE_DEBUG) debug(2, "DestRange for %s: %d-%d", ctx->cli_ctrl->peer, (int) ctx->srv_lrng, (int) ctx->srv_urng); #endif - l = config_port(ctx->username, "ActiveMinDataPort", 0); - u = config_port(ctx->username, "ActiveMaxDataPort", 0); + l = config_port(ctx->username, "ActiveMinDataPort", INPORT_ANY); + u = config_port(ctx->username, "ActiveMaxDataPort", INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->act_lrng = l; ctx->act_urng = u; } else { - ctx->act_lrng = (IPPORT_FTP - 1); - ctx->act_urng = (IPPORT_FTP - 1); + /* do not try to bind a port < 1024 if running as UID != 0 */ + if(0 == getuid()) { + ctx->act_lrng = (IPPORT_FTP - 1); + ctx->act_urng = (IPPORT_FTP - 1); + } else { + ctx->act_lrng = INPORT_ANY; + ctx->act_urng = INPORT_ANY; + } } #if defined(COMPILE_DEBUG) debug(2, "ActiveRange for %s: %d-%d", ctx->cli_ctrl->peer, (int) ctx->act_lrng, (int) ctx->act_urng); #endif - l = config_port(ctx->username, "PassiveMinDataPort", 0); - u = config_port(ctx->username, "PassiveMaxDataPort", 0); + l = config_port(ctx->username, "PassiveMinDataPort", INPORT_ANY); + u = config_port(ctx->username, "PassiveMaxDataPort", INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->pas_lrng = l; ctx->pas_urng = u; } else { - ctx->pas_lrng = 0; - ctx->pas_urng = 0; + ctx->pas_lrng = INPORT_ANY; + ctx->pas_urng = INPORT_ANY; } #if defined(COMPILE_DEBUG) debug(2, "PassiveRange for %s: %d-%d", ctx->cli_ctrl->peer, @@ -438,15 +453,15 @@ ** Evaluate the port ranges */ p = ldap_attrib(ld, e, "DestinationMinPort", "0"); - l = socket_str2port(p, 0); + l = socket_str2port(p, INPORT_ANY); p = ldap_attrib(ld, e, "DestinationMaxPort", "0"); - u = socket_str2port(p, 0); + u = socket_str2port(p, INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->srv_lrng = l; ctx->srv_urng = u; } else { - ctx->srv_lrng = 0; - ctx->srv_urng = 0; + ctx->srv_lrng = INPORT_ANY; + ctx->srv_urng = INPORT_ANY; } #if defined(COMPILE_DEBUG) debug(2, "DestRange for %s: %d-%d", ctx->cli_ctrl->peer, @@ -454,15 +469,21 @@ #endif p = ldap_attrib(ld, e, "ActiveMinDataPort", "0"); - l = socket_str2port(p, 0); + l = socket_str2port(p, INPORT_ANY); p = ldap_attrib(ld, e, "ActiveMaxDataPort", "0"); - u = socket_str2port(p, 0); + u = socket_str2port(p, INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->act_lrng = l; ctx->act_urng = u; } else { - ctx->act_lrng = (IPPORT_FTP - 1); - ctx->act_urng = (IPPORT_FTP - 1); + /* do not try to bind a port < 1024 if running as UID != 0 */ + if(0 == getuid()) { + ctx->act_lrng = (IPPORT_FTP - 1); + ctx->act_urng = (IPPORT_FTP - 1); + } else { + ctx->act_lrng = INPORT_ANY; + ctx->act_urng = INPORT_ANY; + } } #if defined(COMPILE_DEBUG) debug(2, "ActiveRange for %s: %d-%d", ctx->cli_ctrl->peer, @@ -470,15 +491,15 @@ #endif p = ldap_attrib(ld, e, "PassiveMinDataPort", "0"); - l = socket_str2port(p, 0); + l = socket_str2port(p, INPORT_ANY); p = ldap_attrib(ld, e, "PassiveMaxDataPort", "0"); - u = socket_str2port(p, 0); + u = socket_str2port(p, INPORT_ANY); if (l > 0 && u > 0 && u >= l) { ctx->pas_lrng = l; ctx->pas_urng = u; } else { - ctx->pas_lrng = 0; - ctx->pas_urng = 0; + ctx->pas_lrng = INPORT_ANY; + ctx->pas_urng = INPORT_ANY; } #if defined(COMPILE_DEBUG) debug(2, "PassiveRange for %s: %d-%d", ctx->cli_ctrl->peer, --- ftp-proxy/ftp-main.c +++ ftp-proxy/ftp-main.c 2001/09/19 10:40:42 @@ -75,8 +75,10 @@ #define SELECT_TIMEOUT 60 /* Wake up regularly */ -#define ETC_PATH "/etc/proxy-suite/" -#define DEFAULT_CONFIG ETC_PATH "ftp-proxy.conf" +#if !defined(ETC_DIR) +#define ETC_DIR "/etc/proxy-suite" +#endif +#define DEFAULT_CONFIG ETC_DIR"/ftp-proxy.conf" #if defined(COMPILE_DEBUG) # define DEBUG_FILE "/tmp/ftp-proxy.debug" @@ -248,11 +250,14 @@ /* ** Complain if no default DestinationAddress is given + ** while the AllowTransProxy feature is disabled. */ - if (config_str(NULL, "DestinationAddress", NULL) == NULL) { - syslog_error("can't run without default DestAddr"); - fprintf(stderr, "can't run without default DestAddr"); - exit(EXIT_FAILURE); + if (config_bool(NULL, "AllowTransProxy", 0) == 0) { + if (config_str(NULL, "DestinationAddress", NULL) == NULL) { + syslog_error("can't run without default DestAddr"); + fprintf(stderr, "can't run without default DestAddr"); + exit(EXIT_FAILURE); + } } /* --- ftp-proxy/ftp-proxy.conf.5.in +++ ftp-proxy/ftp-proxy.conf.5.in 2001/09/19 10:40:42 @@ -99,18 +99,33 @@ .B ftp-data port as per .B RFC 959, -for the local end of the socket. See also -.B ActiveMinDataPort. +for the local end of the socket if the proxy is running as root +(user ID 0) or to use a random port. See also +.B ActiveMinDataPort +and +.B User +options. .TP .B ActiveMinDataPort Both user and global context. Defines the minimum local port number used when connecting to the client's data port. See also -.B ActiveMaxDataPort. +.B ActiveMaxDataPort +and +.B User +options. +.TP +.B UseMagicChar +Global context only. Defines the character to use as separator +between user and host[:port] in the target setting of +.B AllowMagicUser +Default is the '@' character. This allows you to use E-Mail +addresses as usernames for login to the ftp server +(i.e. me@mydomain%ftp.server:21 if you set it to %). .TP .B AllowMagicUser Global context only. Defines a flag that when set to -.B yes, true, +.B yes, true, or .B on allows the USER name to be interpreted as @@ -122,7 +137,22 @@ the .B DestinationPort directive below. It should only be activated with "trusted" -users, like in an outgoing FTP proxy scenario. +users, like in an outgoing FTP proxy scenario. See also +.B UseMagicChar +option. +.TP +.B AllowTransProxy +Global context only. Defines a flag that when set to +.B yes, true, +or +.B on +allows to use the proxy as transparent proxy for outgoing ftp. +To get it working you also have to redirect client requests on +a gateway or firewall host (i.e. via ipchains) to the ftp-proxy. +It should only be activated with "trusted" users, like in an +outgoing FTP proxy scenario. You can combine this with the +.B AllowMagicUser +option. .TP .B DenyMessage Global context only. Defines the name of a file which prevents @@ -137,7 +167,8 @@ .B DenyString below. After sending the connection is closed. If no such file exists, the deny mechanism is not triggered altogether. See also -.B DenyString. +.B DenyString +option. .TP .B DenyString Global context only. Defines a string that will be displayed to @@ -148,7 +179,8 @@ file exists. The default is .B "'Service not available'." See also -.B DenyMessage. +.B DenyMessage +option. .TP .B DestinationAddress Both user and global context. Defines where to redirect incoming @@ -162,13 +194,15 @@ Valid both for control and for data connections. Defaults to not binding prior to connecting and listening, so that the system selects an arbitrary ephemeral port. See also -.B DestinationMinPort. +.B DestinationMinPort +option. .TP .B DestinationMinPort Both user and global context. Defines the minimum local port number to be used when opening a connection to the FTP server. See also -.B DestinationMaxPort. +.B DestinationMaxPort +option. .TP .B DestinationPort Both user and global context. Defines the FTP server's control @@ -201,7 +235,21 @@ .B LDAP directory, i.e. the root of the tree containing the FTP-Proxy entries. Defaults to an empty string. See also -.B LDAPIdentifier, LDAPObjectClass, LDAPServer. +.B LDAPIdentifier, LDAPObjectClass, LDAPServer +options. +.TP +.B LDAPBindDN +Defines the distinguished name that is used to (simple) bind +the directory service. Defaults to an empty string (anonymous +bind). See also +.B LDAPBindPW +option. +.TP +.B LDAPBindPW +Defines the credential (password) that is used to (simple) bind +the directory service using distinguished name given in the +.B LDAPBindDN +option. Defaults to an empty string (anonymous bind). .TP .B LDAPIdentifier Global context only. Defines the identification attribute for @@ -211,7 +259,8 @@ defaults to the string .B "'CN'" which is short for "Common Name." See also -.B LDAPBaseDN, LDAPObjectClass, LDAPServer. +.B LDAPBaseDN, LDAPObjectClass, LDAPServer +options. .TP .B LDAPObjectClass Global context only. Defines the @@ -222,7 +271,8 @@ is no default, but a value of .B FTPProxyUser is recommended. See also -.B LDAPBaseDN, LDAPIdentifier, LDAPServer. +.B LDAPBaseDN, LDAPIdentifier, LDAPServer +options. .TP .B LDAPServer Global context only. This is the main option for using an @@ -241,14 +291,29 @@ user cannot be found, the program falls back to the configuration file, but will query only the global values and not the user specific ones. See also -.B LDAPBaseDN, LDAPIdentifier, LDAPObjectClass. +.B LDAPBaseDN, LDAPBindDN, LDAPIdentifier, LDAPObjectClass +options. .TP .B Listen Global context only. Defines the address where the proxy itself opens the listening port. The default is .B 0.0.0.0 which instructs the server to bind to any address. See also -.B Port. +.B Port +option. +.TP +.B SockBindRand +Global context only. Defines a flag that when set to +.B yes, true, +or +.B on +, causes the proxy to use a random port in the specified range +with DestinationMinPort/MaxPort, ActiveMinPort/MaxDataPort, +PassiveMinDataPort/MaxDataPort instead of increment the port +number. See also +.B DestinationMinPort, DestinationMaxPort, PassiveMinDataPort, +.B PassiveMaxDataPort, ActiveMinPort, ActiveMaxPort +options. .TP .B LogDestination Global context only. Defines the destination of the logging @@ -276,7 +341,8 @@ Global context only. Defines the maximum number of clients the proxy will allow concurrently. The valid range for this option is 1 to 512, with a default of 64. See also -.B MaxClientsMessage, MaxClientsString. +.B MaxClientsMessage, MaxClientsString +options. .TP .B MaxClientsMessage Global context only. Defines the name of a file that is displayed @@ -289,7 +355,8 @@ When sending the file, each line is prefixed with .B "'421-'" and variable substitution is applied to it. See also -.B MaxClients, MaxClientsString. +.B MaxClients, MaxClientsString +options. .TP .B MaxClientsString Global context only. Defines a string that will be displayed to @@ -299,7 +366,8 @@ has been exceeded. The default is .B "'Service not available'". See also -.B MaxClients, MaxClientsMessage. +.B MaxClients, MaxClientsMessage +options. .TP .B PassiveMaxDataPort Both user and global context. Defines the maximum local port @@ -312,13 +380,15 @@ If either minimum or maximum value is not given, the program defaults to let the system choose an arbitrary ephemeral port. See also -.B PassiveMinDataPort. +.B PassiveMinDataPort +option. .TP .B PassiveMinDataPort Both user and global context. Defines the minimum local port number used when listening for the client's data connection. See also -.B PassiveMaxDataPort. +.B PassiveMaxDataPort +option. .TP .B PidFile Global context only. Defines the name of a process ID file where @@ -338,14 +408,15 @@ port as per .B RFC 959. See also -.B Listen. +.B Listen +option. .TP .B PortResetsPasv Global context only. Defines the action that is taken when a .B PORT command is received while a passive port is open for listening. If the option is set to -.B yes, true, +.B yes, true, or .B on, (which is also the default) the socket will be closed and the @@ -385,7 +456,9 @@ itself will not change its root; this option is only valid for processes running under .B inetd -control or for those that were forked from the daemon. +control or for those that were forked from the daemon. See also +.B User +option. .TP .B ServerType Global context only. Defines the mode in which the FTP-Proxy @@ -402,7 +475,7 @@ started from inetd. .TP .B TCPWrapper -Global context only. Defines s boolean value which is evaluated +Global context only. Defines a boolean value which is evaluated by the FTP-Proxy running as a standalone daemon only. Saying .B yes, true, or @@ -413,7 +486,17 @@ .B no, false, or .B off -(the default) disable the function. +(the default) disable the function. See also +.B TCPWrapperName +option. +.TP +.B TCPWrapperName +Global context only. Use given +.B name +for TCP-Wrapper checks instead of the programm name (argv[0]). +See also +.B TCPWrapper +option. .TP .B TimeOut Both user and global context. Defines the time in seconds after @@ -445,6 +528,32 @@ Global context only. Defines the UNIX style user ID which is given to the process before it serves clients. Default is to keep the current real user ID. +.sp +If the proxy does not run as a privileged user (root, user ID 0), +it has no permission to bind a socket to port < 1024 or to preform +a +.B chroot(2) +call. +.sp +If your system/kernel supports POSIX process capabilities, +you can write a wrapper, that preforms a chroot, disables all +process privileges except of CAP_NET_BIND_SERVICE and starts +the proxy, so the proxy runs with a privileged user ID (0, root) +but without any privileges except of bind to a port < 1024. +.sp +Marc Heuse has written a tool named +.B compartment +(http://www.suse.de/~marc/compartment.html), that does this job +for you on Linux systems (can be ported to other POSIX systems, +too). +.sp +We are shipping a sample run level script (accordingly to LSB, +the Linux system base standard), that is able to use compartment +if you set a ServerRoot in the ftp-proxy.conf file. +.sp +See also +.B ActiveMinDataPort, ActiveMaxDataPort, ServerRoot +options. .TP .B ValidCommands Both user and global context. Defines the list of allowed @@ -497,7 +606,8 @@ .B "'220-'" and variable substitution is applied to it. If no such file exists it is silently ignored. See also -.B WelcomeString. +.B WelcomeString +option. .TP .B WelcomeString Global context only. Defines the string that is sent to the @@ -510,7 +620,8 @@ .B "'%h FTP server (%v - %b) ready'." .br See also -.B WelcomeMessage. +.B WelcomeMessage +option. .SH FILES @sysconfdir@/ftp-proxy.conf .SH "SEE ALSO" --- ftp-proxy/ftp-proxy.conf.sample +++ ftp-proxy/ftp-proxy.conf.sample 2001/09/19 10:40:42 @@ -4,6 +4,8 @@ # # Sample FTP Proxy Configuration File # +# For more information, see ftp-proxy.conf(5) manual page. +# # The general format is "Keyword Value". # # Any white space at the beginning or end of a line and after @@ -17,8 +19,7 @@ # PassiveMinDataPort, PassiveMaxDataPort, # DestinationAddress, DestinationPort, # DestinationMinPort, DestinationMaxPort, -# DestinationTransferMode, MaxPathLength, -# LegalPathChars, ValidPathPattern. +# DestinationTransferMode # These variables can also be obtained from an LDAP server, in # which case the values from this file are not evaluated any # more. @@ -30,13 +31,29 @@ [-Global-] +# Enable this flag if you want to use a random port in +# the specified range with PassiveMinDataPort/MaxDataPort, +# DestinationMinPort/MaxPort, ActiveMinPort/MaxDataPort +# instead of incrementing the port number. +# +# SockBindRand no + # The following entries select a port range for client DTP # ports in active mode, i.e. when the client sends a PORT -# command. The default is port 20 as per RFC 959. +# command. The default is port 20 as per RFC 959, if the +# proxy is running as root (user ID 0) or a random port. # # ActiveMinDataPort 40000 # ActiveMaxDataPort 40999 +# Defines the character to use as separator between user +# and host[:port] in the target setting of AllowMagicUser +# Default is the '@' character. This allows you to use +# E-Mail addresses as usernames for login to the ftp server +# (i.e. me@mydomain%ftp.server:21 if you set it to %). +# +# UseMagicChar % + # The follwing flag is especially useful for outbound FTP # traffic. It allows to put some "magic" in the USER name. # If set, it enables the USER name to contain the target @@ -45,6 +62,14 @@ # # AllowMagicUser no +# The follwing setting allows you to configure a so called +# transparent proxy for outgoing ftp. To get it working you +# also have to redirect client requests on a gateway or +# firewall host (i.e. via ipchains) to the ftp-proxy. +# You can combine this with the AllowMagicUser flag. +# +# AllowTransProxy no + # This message prevents any login if a file with the given # name exists. Instead the contents of the file will be sent # to the client and the connection closed. Lines are prefixed @@ -105,7 +130,20 @@ # handed to the search functions. We strongly recommend to # do so. This is the "root" of the relevant search tree. # -# LDAPBaseDN dc=domain, dc=tld +# LDAPBaseDN dc=domain,dc=tld + +# +# Use distinguished name to (simple) bind to the directory +# service. If not set, an annonymous bind is used. +# +# LDAPBindDN uid=ftp-proxy,dc=domain,dc=tld + +# +# Use credential (password) to bind to the directory service +# using distinguished name given with LDAPBindDN. If not set, +# an annonymous bind is used. +# +# LDAPBindPW aPassword # The next thing to decide when using LDAP is the attribute # used as the main identificator. Some administrators will --- ftp-proxy/ftp-vers.c +++ ftp-proxy/ftp-vers.c 2001/09/19 10:40:42 @@ -0,0 +1,9 @@ +/* +** Version number and build date. +** Created automatically by changelog. +** Please do not edit this file. +*/ + +static char prog_vers[] = "1.7tp7"; +static char prog_date[] = "2001/06/20 21:05:20"; + --- ftp-proxy/rc.config.ftp-proxy +++ ftp-proxy/rc.config.ftp-proxy 2001/09/19 10:40:42 @@ -0,0 +1,9 @@ +# +# Set this to 'yes', if you want to start the FTP-Proxy +# as Daemon instead via /etc/(x)inetd.conf. +# +# Don't forget to set 'ServerType' to 'standalone' in +# /etc/proxy-suite/ftp-proxy.conf. +# +START_FTP_PROXY=no + --- ftp-proxy/rc.script.ftp-proxy +++ ftp-proxy/rc.script.ftp-proxy 2001/09/19 12:06:45 @@ -0,0 +1,496 @@ +#! /bin/sh +# Copyright (c) 2000,2001 SuSE GmbH Nuernberg, Germany. All rights reserved. +# +# Author: Marius Tomaschewski , 2000,2001 +# +# /etc/init.d/ftp-proxy -- LSB compliant(?) run level script +# +# see also ftp-proxy(8) and ftp-proxy.conf(5) manual pages. +# +### BEGIN INIT INFO +# Provides: ftp-proxy +# Required-Start: $network $remote_fs $syslog $named +# Required-Stop: +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Description: starts SuSE ftp-proxy +### END INIT INFO + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - insufficient privilege +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signalling is not supported) +# are considered a success. + +if [ -r /etc/rc.config ] ; then + . /etc/rc.config +else + echo "$0: no /etc/rc.config found - you may port this script" 1>&2 + exit 1 +fi + +# LSB compliant helper shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v ditto but be verbose in local rc status +# rc_status -v -r ditto and clear the local rc status +# rc_failed set local and overall rc status to failed +# rc_failed set local and overall rc status to +# rc_reset clear local rc status (overall remains) +# rc_exit exit appropriate to overall rc status +if [ -r /etc/rc.status ] ; then + . /etc/rc.status +else + echo "$0: no /etc/rc.status found - you may port this script" 1>&2 + exit 1 +fi + +# Determine the base and follow a runlevel link name. +base=${0##*/} +link=${base#*[SK][0-9][0-9]} + +# Force execution if not called by a runlevel directory. +test $link = $base && START_FTP_PROXY=yes +test "$START_FTP_PROXY" = yes || exit 0 + +# +# defaults +# +FTP_PROXY_CFG="/etc/proxy-suite/ftp-proxy.conf" +FTP_PROXY_CMD="/usr/sbin/ftp-proxy" +FTP_PROXY_NAME=$(basename $FTP_PROXY_CMD) + +# +# check config and binary +# +[ -x $FTP_PROXY_CMD ] || { + echo "$0: unable to find ftp-proxy binary: $FTP_PROXY_CMD" 1>&2 + exit 5 +} +[ -r $FTP_PROXY_CFG ] || { + echo "$0: unable to find ftp-proxy config: $FTP_PROXY_CFG" 1>&2 + exit 6 +} + +# +# utils +# +grep="/bin/grep" +log="/usr/bin/logger" +ldd="/usr/bin/ldd" +awk="/usr/bin/awk" +compartment="/usr/sbin/compartment" +LOGGER="$log -i -t $FTP_PROXY_NAME-boot" +INFO="$LOGGER -p info" +ERROR="$LOGGER -p err" + +# +# init used keywords from config file +# +Group="" +PidFile="/var/run/ftp-proxy.pid" +TCPWrapper="" +LDAPServer="" +ServerRoot="" +ServerType="" +LogDestination="" +DenyMessage="" +MaxClientsMessage="" +WelcomeMessage="" + +read_config () +{ + get_value () + { + key=${1} + shift + val=${@} + echo "$key=\"$val\"" + } + + test -n "$FTP_PROXY_CFG" && \ + test -r "$FTP_PROXY_CFG" && \ + while read line ; do + case "$line" in + \#*|"") + ;; + Group*) + eval $(get_value $line) + ;; + PidFile*) + eval $(get_value $line) + ;; + TCPWrapper*) + eval $(get_value $line) + ;; + LDAPServer*) + eval $(get_value $line) + ;; + ServerRoot*) + eval $(get_value $line) + ;; + ServerType*) + eval $(get_value $line) + ;; + LogDestination*) + eval $(get_value $line) + if test -z "${LogDestination//|*/}" ; then + echo "$0: unable to handle a pipe as LogDestination" 1>&2 + exit 1 + fi + ;; + DenyMessage*) + eval $(get_value $line) + ;; + MaxClientsMessage*) + eval $(get_value $line) + ;; + WelcomeMessage*) + eval $(get_value $line) + ;; + *) + ;; + esac + done < "$FTP_PROXY_CFG" +} + +cpifnewer () +{ + for src in $1; do + dst=$2/$(basename $src) + if [ ! -f $dst ]; then + $INFO "copying missing $dst" + cp -pRf $src $dst || return 1 + elif [ $src -nt $dst -o $src -ot $dst ]; then + test -e "$dst" && rm -f "$dst" + $INFO "updating $dst" + cp -pRf $src $dst || return 1 + fi + done + return 0 +} + +mk_chroot () +{ + dir_mode="-m0755" + state_mode="0775" + + # + # list of directories that should be writeable + # + STATE_DIR="" + + if test -n "$ServerRoot" -a ! -d "$ServerRoot" ; then + test -e "$ServerRoot" && rm -f "$ServerRoot" + $INFO "creating missing ServerRoot directory '$ServerRoot'" + mkdir -p $dir_mode "$ServerRoot" || return 1 + fi + + DIRS="dev etc lib usr/sbin usr/lib" + # add config directory + DIRS="$DIRS "$(dirname "$FTP_PROXY_CFG") + # add message direcrories + test -n "$DenyMessage" && DIRS="$DIRS "$(dirname "$DenyMessage") + test -n "$WelcomeMessage" && DIRS="$DIRS "$(dirname "$WelcomeMessage") + test -n "$MaxClientsMessage" && DIRS="$DIRS "$(dirname "$MaxClientsMessage") + # add Pid-Directory + if test -n "$PidFile" ; then + dir=$(dirname "$PidFile") + DIRS="$DIRS $dir" + STATE_DIR="$STATE_DIR $dir" + else + DIRS="$DIRS var/run" + STATE_DIR="$STATE_DIR var/run" + fi + # add Log-Directory if not a syslog facility + if test -n "$LogDestination" -a -z "${LogDestination//\/*/}" ; then + dir=$(dirname "$LogDestination") + DIRS="$DIRS $dir" + STATE_DIR="$STATE_DIR $dir" + fi + + # ok, create the dirs + test -n "$DIRS" && for dir in $DIRS ; do + test -d "$ServerRoot/$dir" && continue + test -e "$ServerRoot/$dir" && rm -f "$ServerRoot/$dir" + $INFO "creating missing directory $ServerRoot/$dir" + mkdir -p $dir_mode "$ServerRoot/$dir" || return 1 + done + # make sure, state-dirs are writeable after cap's chroot + test -n "$STATE_DIR" && for dir in $STATE_DIR ; do + chmod $state_mode "$ServerRoot/$dir" + chown "root:$Group" "$ServerRoot/$dir" + done + + # create a null device + if test ! -c "$ServerRoot/dev/null" ; then + test -e "$ServerRoot/dev/null" && rm -f "$ServerRoot/dev/null" + $INFO "creating missing device '$ServerRoot/dev/null'" + mknod -m0666 "$ServerRoot/dev/null" c 1 3 || return 1 + fi + + # copy binaries + BINS="$FTP_PROXY_CMD" + test -n "$BINS" && for bin in $BINS ; do + if test -n "$bin" -a -x "$bin" ; then + bdir=$(dirname $bin) + if test ! -d "$ServerRoot/$bdir" ; then + test -e "$ServerRoot/$bdir" && rm -f "$ServerRoot/$bdir" + $INFO "creating missing directory $ServerRoot/$bdir" + mkdir -p $dir_mode "$ServerRoot/$bdir" || return 1 + fi + cpifnewer "$bin" "$ServerRoot/$bdir" || return 1 + + # + # copy all libs ldd says they are needed for the binary + # + LIB=$($ldd "$bin" 2> /dev/null | $grep -v "not a "| $awk '{print$ 3}') + for lib in $LIB ; do + ldir=$(dirname $lib) + if test ! -d "$ServerRoot/$ldir" ; then + test -e "$ServerRoot/$ldir" && rm -f "$ServerRoot/$ldir" + $INFO "creating missing directory $ServerRoot/$ldir" + mkdir -p $dir_mode "$ServerRoot/$ldir" || return 1 + fi + cpifnewer "$lib" "$ServerRoot/$ldir" || return 1 + done + else + $INFO "no such file or directory '$bin'" + return 1 + fi + done + + # handle /etc/localtime link + if [ -L /etc/localtime ]; then + test -d "$ServerRoot/usr/share/zoneinfo/"$(dirname "$TIMEZONE") || \ + mkdir -p $dir_mode "$ServerRoot/usr/share/zoneinfo/"$(dirname "$TIMEZONE") + cpifnewer /usr/share/zoneinfo/$TIMEZONE "$ServerRoot/usr/share/zoneinfo" + ln -sf ../usr/share/zoneinfo/$TIMEZONE "$ServerRoot/etc/localtime" + else + cpifnewer /etc/localtime "$ServerRoot/etc" + fi + + CONF="/etc/host.conf /etc/resolv.conf /etc/nsswitch.conf /etc/services /etc/hosts /etc/protocols /etc/passwd /etc/group" + + if test -n "$TCPWrapper" -a "$TCPWrapper" = yes ; then + CONF="$CONF /etc/hosts.allow /etc/hosts.deny" + fi + test -n "$LDAPServer" && CONF="$CONF /etc/openldap/ldap.conf" + test -n "$DenyMessage" && CONF="$CONF $DenyMessage" + test -n "$WelcomeMessage" && CONF="$CONF $WelcomeMessage" + test -n "$MaxClientsMessage" && CONF="$CONF $MaxClientsMessage" + + # copy/update (config) files + test -n "$CONF" && for cfg in $CONF ; do + if test -n "$cfg" -a -e "$cfg" ; then + cdir=$(dirname $cfg) + if test ! -d "$ServerRoot/$cdir" ; then + test -e "$ServerRoot/$cdir" && rm -f "$ServerRoot/$cdir" + $INFO "creating missing directory $ServerRoot/$cdir" + mkdir -p $dir_mode "$ServerRoot/$cdir" || return 1 + fi + cpifnewer "$cfg" "$ServerRoot/$cdir" || return 1 + else + $INFO "no such file or directory '$cfg'" + return 1 + fi + done + + # copy/update libraries + LIBS="/lib/libnss* /lib/libresolv* /lib/libdb*" + test -n "$LIBS" && for lib in $LIBS ; do + if test -n "$lib" -a -x "$lib" ; then + ldir=$(dirname $lib) + if test ! -d "$ServerRoot/$ldir" ; then + test -e "$ServerRoot/$ldir" && rm -f "$ServerRoot/$ldir" + $INFO "creating missing directory $ServerRoot/$ldir" + mkdir -p $dir_mode "$ServerRoot/$ldir" || return 1 + fi + cpifnewer "$lib" "$ServerRoot/$ldir" || return 1 + else + $INFO "no such file or directory '$lib'" + return 1 + fi + done + + if test -r "$FTP_PROXY_CFG" ; then + if test -e "$ServerRoot/$FTP_PROXY_CFG" ; then + rm -f "$ServerRoot/$FTP_PROXY_CFG" + fi + while read line ; do + case "$line" in + \#*|"") ;; + Group*) ;; + User*) ;; + ServerRoot*) ;; + *) echo $line ;; + esac + done < "$FTP_PROXY_CFG" > "$ServerRoot/$FTP_PROXY_CFG" + fi + + if test ! -f "$ServerRoot/etc/ld.so.conf" ; then + touch "$ServerRoot/etc/ld.so.conf" + fi + + # + # rebuild /etc/ld.so.cache + # + ldconfig -X -r "$ServerRoot" 2>&1 | $INFO + + return 0 +} + +syslog_restart () +{ + # + # restart syslog so it creates a log-sockets in chroot's + # restart is needed because syslogd uses arguments to + # set additional sockets + # + if [ "$START_SYSLOG_NG" = "yes" -a "$SYSLOG_NG_REPLACE" = "yes" ] ; then + rcsyslog-ng restart + else + rcsyslog restart + fi +} + +# +# read config file +# +read_config + +# reset status of this service +rc_reset + +case "$1" in + +chroot) + # check if we have all needed utils + if test -n "$ServerRoot" -a -n "$Group" ; then + [ -x "$grep" -a -x "$log" -a -x "$ldd" -a -x "$awk" ] || { + echo "$0: unable to find all needed utilities" 1>&2 + exit 1 + } + echo -n "Checking $FTP_PROXY_NAME chroot (in $ServerRoot): " + mk_chroot + rc_status -v + else + echo "$0: set ServerRoot and Group in $FTP_PROXY_CFG" 1>&2 + exit 1 + fi + +;; +start) + if test -z "$ServerType" -o "$ServerType" != "standalone" ; then + echo "$0: ServerType is not standalone in $FTP_PROXY_CFG" 1>&2 + exit 1 + fi + + if test -n "$ServerRoot" -a -x "$compartment" ; then + # prepare/update chroot environment + $0 chroot || exit 1 + + # handle a syslog facility in LogDestination + if test -n "${LogDestination//\/*/}" -a \ + ! -S "$ServerRoot/dev/log" ; then + syslog_restart + if test ! -S "$ServerRoot/dev/log" ; then + echo "you may add $ServerRoot/dev/log socket " 1>&2 + echo "to SYSLOGD_PARMS variable in /etc/rc.config" 1>&2 + exit 1 + fi + fi + + # "boot" the proxy ;-) + echo -n "Starting $FTP_PROXY_NAME (in $ServerRoot): " + chroot_group="--group $Group" + PATH="/bin:/sbin:/usr/bin:/usr/sbin" \ + $compartment \ + --fork --cap CAP_NET_BIND_SERVICE \ + --chroot "$ServerRoot" $chroot_group \ + "$FTP_PROXY_CMD" -f "$FTP_PROXY_CFG" + rc_status -v + else + echo -n "Starting $FTP_PROXY_NAME: " + startproc "$FTP_PROXY_CMD" -f "$FTP_PROXY_CFG" + rc_status -v + fi + +;; +stop) + ## Stop the service + echo -n "Shutting down $FTP_PROXY_NAME: " + [ -x "$ServerRoot/$FTP_PROXY_CMD" ] || ServerRoot="" + killproc -TERM "$ServerRoot/$FTP_PROXY_CMD" + rc_status -v + +;; +try-restart) + ## Stop the service and if this succeeds (i.e. + ## the service was running before), start it again. + ## Note: try-restart is not (yet) part of LSB (as of 0.7.5) + $0 status >/dev/null && $0 restart + rc_status + +;; +restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + rc_status + +;; +force-reload) + ## Signal the daemon to reload its config. + ## If it does not support it, restart. + echo -n "Reload the $FTP_PROXY_NAME configuration: " + [ -x "$ServerRoot/$FTP_PROXY_CMD" ] || ServerRoot="" + killproc -HUP "$ServerRoot/$FTP_PROXY_CMD" + rc_status -v + +;; +reload) + ## Like force-reload, but if daemon does not + ## support signalling, do nothing (!) + echo -n "Reload the $FTP_PROXY_NAME configuration: " + [ -x "$ServerRoot/$FTP_PROXY_CMD" ] || ServerRoot="" + killproc -HUP "$ServerRoot/$FTP_PROXY_CMD" + rc_status -v + +;; +status) + echo -n "Checking for $FTP_PROXY_NAME: " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + [ -x "$ServerRoot/$FTP_PROXY_CMD" ] || ServerRoot="" + checkproc "$ServerRoot/$FTP_PROXY_CMD" + rc_status -v + +;; +probe) + ## Optional: Probe for the necessity of a reload, + ## give out the argument which is required for a reload. + [ -x "$ServerRoot/$FTP_PROXY_CMD" ] || ServerRoot="" + test "$ServerRoot/$FTP_PROXY_CFG" -nt \ + "$ServerRoot/$PidFile" && echo reload + +;; +*) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" + exit 1 + ;; +esac + +# Inform the caller not only verbosely and set an exit status. +rc_exit +