============== v0.27 - Fri Sep 22 19:41:33 ART 2000 . fixed TCP extended options handling in ip_masq helper modules . ip_masq_ftp: reimplemented parsing logic, merged portfw FTP support for PASV (new "fwports" module param), use th->doff for data offset, safe_mem_eq2() and size adjustments for less CPU usage . ip_masq_app: arrange flags for INBOUND calling also ============== --- linux/include/net/ip_masq.h.dist Mon Sep 18 09:45:07 2000 +++ linux/include/net/ip_masq.h Fri Sep 22 12:04:33 2000 @@ -188,6 +188,17 @@ extern int unregister_ip_masq_app(struct ip_masq_app *mapp); /* + * ip_masq_app flags[8bits] (ORed with protocol[8bits] ) + */ +#define IP_MASQ_APP_OUTBOUND 0x0100 /* dst port matched (in-out) */ +#define IP_MASQ_APP_INBOUND 0x0200 /* src port matched (in-out) */ +#define IP_MASQ_APP_FLAGS_MASK 0xff00 /* mask for app flags */ + +#define IP_MASQ_APP_TYPE(proto, port) ( (proto)<<16 | (port) ) +#define IP_MASQ_APP_TYPE2PORT(type) ( (type) & 0xffff ) +#define IP_MASQ_APP_TYPE2PROTO(type) ( ((type)>>16) & 0x00ff ) +#define IP_MASQ_APP_TYPE2FLAGS(type) ( ((type)>>16) & 0xff00 ) +/* * get ip_masq_app obj by proto,port(net_byte_order) */ extern struct ip_masq_app * ip_masq_app_get(unsigned short proto, __u16 port); --- linux/net/ipv4/ip_masq_app.c.dist Sun Oct 4 14:21:45 1998 +++ linux/net/ipv4/ip_masq_app.c Fri Sep 22 16:58:01 2000 @@ -15,6 +15,7 @@ * Fixes: * JJC : Implemented also input pkt hook * Miquel van Smoorenburg : Copy more stuff when resizing skb + * JJC : add INBOUND hooks (default to OUTBOUND) * * * FIXME: @@ -41,10 +42,19 @@ #define IP_MASQ_APP_TAB_SIZE 16 /* must be power of 2 */ -#define IP_MASQ_APP_HASH(proto, port) ((port^proto) & (IP_MASQ_APP_TAB_SIZE-1)) -#define IP_MASQ_APP_TYPE(proto, port) ( proto<<16 | port ) -#define IP_MASQ_APP_PORT(type) ( type & 0xffff ) -#define IP_MASQ_APP_PROTO(type) ( (type>>16) & 0x00ff ) +/* + * ip_masq_app->type holds all the values needed to hook + * passing packet + * flags: INBOUND, OUTBOUMD + * proto: IP_PROTO {TCP, UDP} + * port: application port + * + * <----------- 32 bits ------------> + * [ flags ][ proto ][ port ] + * + */ + +#define IP_MASQ_APP_HASH(proto, port) (((port)^(proto)) & (IP_MASQ_APP_TAB_SIZE-1)) EXPORT_SYMBOL(register_ip_masq_app); @@ -60,6 +70,7 @@ /* * ip_masq_app registration routine * port: host byte order. + * proto: (flags<<8) | ipproto */ int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 port) @@ -70,9 +81,24 @@ IP_MASQ_ERR("register_ip_masq_app(): NULL arg\n"); return -EINVAL; } + + /* + * (compatbl) Hack module registration to "OUTBOUND" lookups + * for modules that don't explicitly setup flags + */ + if (!(proto & IP_MASQ_APP_FLAGS_MASK)) + proto |= IP_MASQ_APP_OUTBOUND; + mapp->type = IP_MASQ_APP_TYPE(proto, port); + + IP_MASQ_DEBUG(1,"ip_masq_app_register(): flags=0x%x, proto=%d, port=%d\n", + IP_MASQ_APP_TYPE2FLAGS(mapp->type), + IP_MASQ_APP_TYPE2PROTO(mapp->type), + IP_MASQ_APP_TYPE2PORT(mapp->type)); + mapp->n_attach = 0; hash = IP_MASQ_APP_HASH(proto, port); + IP_MASQ_DEBUG(2, "ip_masq_app_register(): type=0x%x hash=0x%x\n", mapp->type, hash); save_flags(flags); cli(); @@ -104,7 +130,7 @@ mapp->n_attach); return -EINVAL; } - hash = IP_MASQ_APP_HASH(IP_MASQ_APP_PROTO(mapp->type), IP_MASQ_APP_PORT(mapp->type)); + hash = IP_MASQ_APP_HASH(IP_MASQ_APP_TYPE2PROTO(mapp->type), IP_MASQ_APP_TYPE2PORT(mapp->type)); save_flags(flags); cli(); @@ -116,8 +142,11 @@ } restore_flags(flags); - IP_MASQ_ERR("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type)); + IP_MASQ_ERR("unregister_ip_masq_app(flags=0x%x, proto=%s,port=%u): not hashed!\n", + + IP_MASQ_APP_TYPE2FLAGS(mapp->type), + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type)); return -EINVAL; } @@ -134,6 +163,7 @@ port = ntohs(port); type = IP_MASQ_APP_TYPE(proto,port); hash = IP_MASQ_APP_HASH(proto,port); + IP_MASQ_DEBUG(2, "ip_masq_app_get(): type=0x%x hash=0x%x\n", type, hash); for(mapp = ip_masq_app_base[hash]; mapp ; mapp = mapp->next) { if (type == mapp->type) return mapp; } @@ -159,8 +189,8 @@ if (n_at < 0) { restore_flags(flags); IP_MASQ_ERR("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), - IP_MASQ_APP_PORT(mapp->type)); + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type)); return -1; } mapp->n_attach = n_at; @@ -171,6 +201,7 @@ /* * Bind ip_masq to its ip_masq_app based on proto and dport ALREADY * set in ip_masq struct. Also calls constructor. + * Only called from ip_masq_new() when creating NEW masq tunnel, */ struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms) @@ -180,14 +211,23 @@ if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP) return NULL; - mapp = ip_masq_app_get(ms->protocol, ms->dport); + IP_MASQ_DEBUG(1, "ip_masq_bind_app() : called for dst=%d.%d.%d.%d:%d src=%d.%d.%d.%d:%d masq=%d.%d.%d.%d:%d\n", + NIPQUAD(ms->daddr), ntohs(ms->dport), + NIPQUAD(ms->saddr), ntohs(ms->sport), + NIPQUAD(ms->maddr), ntohs(ms->mport)); -#if 0000 -/* #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW */ - if (mapp == NULL) - mapp = ip_masq_app_get(ms->protocol, ms->sport); -/* #endif */ -#endif + /* + * Lookup "normal" case (client's active open inside) + */ + mapp = ip_masq_app_get(IP_MASQ_APP_OUTBOUND|ms->protocol, ms->dport); + + if (mapp==NULL) { + /* + * lookup "reverse" case (server waiting inside, + * for portfw and friends) + */ + mapp = ip_masq_app_get(IP_MASQ_APP_INBOUND|ms->protocol, ms->mport); + } if (mapp != NULL) { /* @@ -198,6 +238,8 @@ IP_MASQ_ERR("ip_masq_bind_app() called for already bound object.\n"); return ms->app; } + IP_MASQ_DEBUG(1, "ip_masq_bind_app() : bounded \"%s\" module\n", + mapp->name); ms->app = mapp; if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms); @@ -455,8 +497,8 @@ continue; len += sprintf(buffer+len, "%-3s %-7u %-7d %-17s\n", - masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), - IP_MASQ_APP_PORT(mapp->type), mapp->n_attach, + masq_proto_name(IP_MASQ_APP_TYPE2PROTO(mapp->type)), + IP_MASQ_APP_TYPE2PORT(mapp->type), mapp->n_attach, mapp->name); if(len >= length) @@ -497,7 +539,7 @@ * Replace a segment (of skb->data) with a new one. * FIXME: Should re-use same skb if space available, this could * be done if n_len < o_len, unless some extra space - * were already allocated at driver level :P . + * were already allocated at link level :P . */ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len) --- linux/net/ipv4/ip_masq_ftp.c.dist Wed May 3 21:16:53 2000 +++ linux/net/ipv4/ip_masq_ftp.c Fri Sep 22 19:38:51 2000 @@ -2,7 +2,7 @@ * IP_MASQ_FTP ftp masquerading module * * - * Version: @(#)ip_masq_ftp.c 0.04 02/05/96 + * Version: @(#)ip_masq_ftp.c 0.10 20/09/00 * * Author: Wouter Gadeyne * @@ -17,12 +17,13 @@ * Juan Jose Ciarlante : use ip_masq_listen() * Juan Jose Ciarlante : use private app_data for own flag(s) * Bjarni R. Einarsson : Added protection against "extended FTP ALG attack" - * + * Juan Jose Ciarlante : reimplemented parsing logic, merged portfw FTP support for PASV (new "fwports" module param), use th->doff for data offset + * Juan Jose Ciarlante : safe_mem_eq2() and size adjustments for less CPU * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * 2 of the License. * * Multiple Port Support * The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12) @@ -34,6 +35,16 @@ * /etc/conf.modules (or /etc/modules.conf depending on your config) * where modload will pick it up should you use modload to load your * modules. + * Additional portfw Port Support + * Module parameter "fwports" specifies the list of forwarded ports + * at firewall (portfw and friends) that must be hooked to allow + * PASV connections to inside servers. + * Same as before: + * fwports=fw1,fw2,... + * Eg: + * ipmasqadm portfw -a -P tcp -L a.b.c.d 2021 -R 192.168.1.1 21 + * ipmasqadm portfw -a -P tcp -L a.b.c.d 8021 -R 192.168.1.1 21 + * modprobe ip_masq_ftp fwports=2021,8021 * * Protection against the "extended FTP ALG vulnerability". * This vulnerability was reported in: @@ -67,12 +78,13 @@ #include +#define IP_MASQ_FTP_RPAREN 0x01 /* stream has ')' char */ /* * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper * First port is set to the default port. */ static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */ -struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS]; +static struct ip_masq_app *mapp_ports[MAX_MASQ_APP_PORTS]; /* * List of ports (up to MAX_MASQ_APP_PORTS) we don't allow ftp-data @@ -82,6 +94,13 @@ static int noport[MAX_MASQ_APP_PORTS] = {6000, 0}; /* I rely on the trailing items being set to zero */ /* + * in (forwarded) ports + */ +static int fwports[MAX_MASQ_APP_PORTS] = {0}; +static struct ip_masq_app *mapp_fwports[MAX_MASQ_APP_PORTS]; + + +/* * Debug level */ #ifdef CONFIG_IP_MASQ_DEBUG @@ -90,11 +109,53 @@ #endif MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); +MODULE_PARM(fwports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); MODULE_PARM(noport, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i"); /* Dummy variable */ static int masq_ftp_pasv; +/* + * This function parses the IP address and Port number found in PORT commands + * and PASV responses. This used to be done in-line, but with four cases it + * seemed worth encapsulating. It returns the IP address, or zero if an + * error is detected. + */ +static __u32 parse_ip_port( char **datap, __u16 *portp ) +{ + char *data = *datap; +#if CONFIG_IP_MASQ_DEBUG + char *data0=data; +#endif + unsigned char p1,p2,p3,p4,p5,p6; + + p1 = simple_strtoul(data, &data, 10); + if (*data != ',') + return 0; + p2 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p3 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p4 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p5 = simple_strtoul(data+1, &data, 10); + if (*data != ',') + return 0; + p6 = simple_strtoul(data+1, &data, 10); + + IP_MASQ_DEBUG(2-debug, "FTP: parse_ip_port() Ok: \"%*s\" size=%d\n", + data-data0, + data0, + data-data0); + *datap = data; + *portp = (p5<<8) | p6; + return (p1<<24) | (p2<<16) | (p3<<8) | p4; +} + + static int masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms) { @@ -109,138 +170,221 @@ return 0; } + +static int masq_ftp_unsafe(__u32 from_ip, __u16 from_port) { + int i; + if (from_port < 1024) + { + IP_MASQ_DEBUG(1-debug, "Unsafe PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port); + return 1; + } + + for (i = 0; (i < MAX_MASQ_APP_PORTS) && (noport[i]); i++) + if (from_port == noport[i]) + { + IP_MASQ_DEBUG(1-debug, "Unsafe (module parm) PORT %d.%d.%d.%d:%d detected, ignored\n",NIPQUAD(from_ip),from_port); + return 1; + } + return 0; +} +/* + * carefully compare with any of these 2 strings, eating stream pointer + * as it proceeds + * Returns: + * NULL not matched + * !NULL last matched char* + */ +static char* safe_mem_eq2(char *data, const char *data_limit, int size, const char *str1, const char *str2) { +#if CONFIG_IP_MASQ_DEBUG + const char *data0=data; + if (!data) { + IP_MASQ_ERR("FTP: NULL data passed to safe_mem_eq2()!!!"); + return NULL; + } +#endif + IP_MASQ_DEBUG(3-debug, "FTP: safe_mem_equal(): datalimit-data=%d, size=%d\n", data_limit-data, size); + + /* No point in going after data_limit for "size" comparison */ + data_limit -= size; + + while (data <= data_limit) { + if (memcmp(data,str1,size)==0) + goto equal_ok; + if (memcmp(data,str2,size)==0) + goto equal_ok; + data++; + } + IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" not matched)\n", str1); + return NULL; +equal_ok: + IP_MASQ_DEBUG(2-debug, "FTP: safe_mem_equal(): \"%s\" matched at offset %d\n", + str1, data-data0); + return data+size; +} int masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr) { struct sk_buff *skb; struct iphdr *iph; struct tcphdr *th; - char *p, *data, *data_limit; - unsigned char p1,p2,p3,p4,p5,p6; - __u32 from; + char *p, *data, *data0, *data_limit; + __u32 from=0; + __u32 from_n; __u16 port; struct ip_masq *n_ms; - char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ + char buf[25]; /* xxx.xxx.xxx.xxx,ppp,ppp)\000 */ + unsigned flags=0; /* processing flags */ unsigned buf_len; - int diff, i, unsafe; + int diff=0; + + /* Only useful for established sessions */ + if (ms->state != IP_MASQ_S_ESTABLISHED) + return 0; skb = *skb_p; iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); - data = (char *)&th[1]; + data = (char *)th + (((struct tcphdr*)th)->doff << 2); + data0 = data; + data_limit = skb->h.raw + skb->len; + + IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_out() for type=0x%x datasize=%d\n", + mapp->type, + data_limit-data); - data_limit = skb->h.raw + skb->len - 18; - if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0)) - ms->app_data = &masq_ftp_pasv; + /* Only useful for actual data */ + if (data_limit<=data) + return 0; - while (data < data_limit) - { - if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5)) - { - data ++; - continue; + /* + * We are about to hack an OUT (from firewall) packet, + * check if + * OUTBOUND: internal client stream + * INBOUND: internal server stream + */ + + if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: in->out client stream\n"); + + /* + * Minimum sizes ... + * PORT x,x,x,x,y,y+... + \r\n + * <---------- 11 + 2 = 13 + */ + if (!(data=safe_mem_eq2(data, data_limit - 13, + 5, "PORT ", "port ")) ) { + if (safe_mem_eq2(data0, data_limit, 6, "PASV\r\n", "pasv\r\n")) { + /* Flags this tunnel as pasv, return */ + ms->app_data = &masq_ftp_pasv; + } + return 0; } - p = data+5; - p1 = simple_strtoul(data+5,&data,10); - if (*data!=',') - continue; - p2 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p3 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p4 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p5 = simple_strtoul(data+1,&data,10); - if (*data!=',') - continue; - p6 = simple_strtoul(data+1,&data,10); - if (*data!='\r' && *data!='\n') - continue; + p = data; + from = parse_ip_port(&data, &port); + if (masq_ftp_unsafe(from,port)) + return 0; + IP_MASQ_DEBUG(1-debug,"FTP: out: PORT %d.%d.%d.%d:%d detected\n", + NIPQUAD(from), port); - from = (p1<<24) | (p2<<16) | (p3<<8) | p4; - port = (p5<<8) | p6; + } else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: in->out server stream\n"); - if (port < 1024) - { - IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port); - continue; - } + /* + * Minimum sizes... + * Entering Passive Mode (x,x,x,x,y,y)+... + \r\n + * <------------------------- 26 + 2 = 28 + * <----------------- 18 + 2 = 20 + * <------------ 13 + 2 = 15 + */ + if (!(data=safe_mem_eq2(data, data_limit-28, 8, "ntering ", "NTERING "))) + return 0; + if (!(data=safe_mem_eq2(data+1, data_limit-20, 7, "assive ", "ASSIVE "))) + return 0; + if (!(data=safe_mem_eq2(data+1, data_limit-15, 4, "ode ", "ODE "))) + return 0; + do { + if (data >= data_limit) + return 0; + } while (*data++ != '('); - for (unsafe = i = 0; (i < MAX_MASQ_APP_PORTS) && (noport[i]); i++) - if (port == noport[i]) - { - IP_MASQ_DEBUG(1-debug, "Unsafe PORT %X:%X detected, ignored\n",from,port); - unsafe = 1; - } - if (unsafe) continue; + p = data; + from = parse_ip_port(&data, &port); + if ((from == 0) || (*data++ !=')')) + return 0; - IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port); + flags |= IP_MASQ_FTP_RPAREN; - /* - * Now update or create an masquerade entry for it - */ + } else + return 0; - IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0); + /* no from detected, give up */ + if (from == 0) + return 0; - n_ms = ip_masq_out_get(iph->protocol, - htonl(from), htons(port), - iph->daddr, 0); - if (!n_ms) { - n_ms = ip_masq_new(IPPROTO_TCP, - maddr, 0, - htonl(from), htons(port), - iph->daddr, 0, - IP_MASQ_F_NO_DPORT); + /* store from in network byte order */ + from_n = htonl(from); + /* + * Now update or create an masquerade entry for it + */ - if (n_ms==NULL) - return 0; - ip_masq_control_add(n_ms, ms); - } + IP_MASQ_DEBUG(1-debug, "FTP: out: %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", + NIPQUAD(from_n), htons(port), + NIPQUAD(iph->daddr), 0); - /* - * Replace the old PORT with the new one - */ - from = ntohl(n_ms->maddr); - port = ntohs(n_ms->mport); - sprintf(buf,"%d,%d,%d,%d,%d,%d", - from>>24&255,from>>16&255,from>>8&255,from&255, - port>>8&255,port&255); - buf_len = strlen(buf); + n_ms = ip_masq_out_get(iph->protocol, + from_n, htons(port), + iph->daddr, 0); + if (!n_ms) { + n_ms = ip_masq_new(IPPROTO_TCP, + maddr, 0, + from_n, htons(port), + iph->daddr, 0, + IP_MASQ_F_NO_DPORT); - IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port); + if (n_ms==NULL) + return 0; + ip_masq_control_add(n_ms, ms); + } - /* - * Calculate required delta-offset to keep TCP happy - */ - - diff = buf_len - (data-p); - - /* - * No shift. - */ - - if (diff==0) { - /* - * simple case, just replace the old PORT cmd - */ - memcpy(p,buf,buf_len); - } else { + /* + * Replace the old PORT with the new one + */ + from = ntohl(n_ms->maddr); + port = ntohs(n_ms->mport); + sprintf(buf,"%d,%d,%d,%d,%d,%d%c", + from>>24&255,from>>16&255,from>>8&255,from&255, + port>>8&255,port&255, + (flags&IP_MASQ_FTP_RPAREN)? ')':0); + buf_len = strlen(buf); - *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); - } - /* - * Move tunnel to listen state - */ - ip_masq_listen(n_ms); - ip_masq_put(n_ms); + IP_MASQ_DEBUG(1-debug, "FTP: new PORT %d.%d.%d.%d:%d\n",NIPQUAD(maddr),port); - return diff; + /* + * Calculate required delta-offset to keep TCP happy + */ + + diff = buf_len - (data-p); + + /* + * No shift. + */ + if (diff==0) { + /* + * simple case, just replace the old PORT cmd + */ + memcpy(p,buf,buf_len); + } else { + + *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len); } - return 0; + /* + * Move tunnel to listen state + */ + ip_masq_listen(n_ms); + ip_masq_put(n_ms); + + return diff; } @@ -269,53 +413,86 @@ struct iphdr *iph; struct tcphdr *th; char *data, *data_limit; - unsigned char p1,p2,p3,p4,p5,p6; __u32 to; + __u32 from_n; __u16 port; struct ip_masq *n_ms; - if (ms->app_data != &masq_ftp_pasv) - return 0; /* quick exit if no outstanding PASV */ + /* Only useful for established sessions */ + if (ms->state != IP_MASQ_S_ESTABLISHED) + return 0; skb = *skb_p; iph = skb->nh.iph; th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); - data = (char *)&th[1]; + data = (char *)th + (((struct tcphdr*)th)->doff << 2); data_limit = skb->h.raw + skb->len; - while (data < data_limit && *data != ' ') - ++data; - while (data < data_limit && *data == ' ') - ++data; - data += 22; - if (data >= data_limit || *data != '(') - return 0; - p1 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p2 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p3 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p4 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p5 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ',') - return 0; - p6 = simple_strtoul(data+1, &data, 10); - if (data >= data_limit || *data != ')') + IP_MASQ_DEBUG(2-debug, "FTP: called masq_ftp_in() for type=0x%x datasize=%d\n", + mapp->type, + data_limit-data); + + /* Only useful for actual data */ + if (data_limit<=data) return 0; - to = (p1<<24) | (p2<<16) | (p3<<8) | p4; - port = (p5<<8) | p6; + /* + * We are about to hack an IN (to firewall) packet, + * check if + * OUTBOUND: internal client stream + * INBOUND: internal server stream + */ + + if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_OUTBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: out->in client stream\n"); + /* + * For OUTBOUND only parse on input for linking PASV + * data tunnel with control. + * Exit quickly if no outstanding PASV + */ + if (ms->app_data != &masq_ftp_pasv) + return 0; + + /* + * Minimum sizes... + * Entering Passive Mode (x,x,x,x,y,y)+... + \r\n + * <------------------------- 26 + 2 = 28 + * <----------------- 18 + 2 = 20 + * <------------ 13 + 2 = 15 + */ + if (!(data=safe_mem_eq2(data, data_limit-28, 8, "ntering ", "NTERING "))) + return 0; + if (!(data=safe_mem_eq2(data+1, data_limit-20, 6, "ssive ", "SSIVE "))) + return 0; + if (!(data=safe_mem_eq2(data+1, data_limit-15, 4, "ode ", "ODE "))) + return 0; + do { + if (data >= data_limit) + return 0; + } while (*data++ != '('); + + to = parse_ip_port(&data, &port); + if (to == 0 || *data != ')') + return 0; + + } else if (IP_MASQ_APP_TYPE2FLAGS(mapp->type)&IP_MASQ_APP_INBOUND) { + IP_MASQ_DEBUG(1-debug, "FTP: out->in server stream\n"); + if (!(data=safe_mem_eq2(data, data_limit, 5, "PORT ", "port "))) + return 0; + to = parse_ip_port(&data, &port); + if (to == 0 || (*data != '\r' && *data != '\n')) + return 0; + + } else + return 0; /* * Now update or create an masquerade entry for it */ - IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port); + from_n = ntohl(ms->saddr); + IP_MASQ_DEBUG(1-debug, "PASV response %d.%d.%d.%d:%d %d.%d.%d.%d:%d detected\n", + NIPQUAD(from_n), 0, + NIPQUAD(to), port); n_ms = ip_masq_out_get(iph->protocol, ms->saddr, 0, @@ -357,57 +534,72 @@ masq_ftp_in, /* pkt_in */ }; -/* - * ip_masq_ftp initialization - */ - -__initfunc(int ip_masq_ftp_init(void)) -{ +static int register_ports(struct ip_masq_app *mapp_instances[], int *ports, unsigned proto) { int i, j; - for (i=0; (itype); } else { /* To be safe, force the incarnation table entry to NULL */ - masq_incarnations[i] = NULL; + mapp_instances[i] = NULL; } } return 0; } - -/* - * ip_masq_ftp fin. - */ - -int ip_masq_ftp_done(void) -{ +static int unregister_ports(struct ip_masq_app *mapp_instances[]) { int i, j, k; k=0; for (i=0; (itype); + kfree(mapp_instances[i]); + mapp_instances[i] = NULL; } } } return k; +} + +/* + * ip_masq_ftp initialization + */ + +__initfunc(int ip_masq_ftp_init(void)) +{ + int ret; + if ((ret = register_ports(mapp_ports, ports, + IPPROTO_TCP|IP_MASQ_APP_OUTBOUND))) + return ret; + if ((ret = register_ports(mapp_fwports, fwports, + IPPROTO_TCP|IP_MASQ_APP_INBOUND))) + return ret; + return 0; +} + +/* + * ip_masq_ftp fin. + */ + +int ip_masq_ftp_done(void) +{ + int ret; + ret = unregister_ports(mapp_ports); + ret += unregister_ports(mapp_fwports); + return ret; } #ifdef MODULE --- linux/net/ipv4/ip_masq_irc.c.dist Mon Sep 18 09:38:19 2000 +++ linux/net/ipv4/ip_masq_irc.c Tue Sep 19 17:00:01 2000 @@ -77,7 +77,7 @@ * List of supported DCC protocols */ -#define NUM_DCCPROTO 5 +#define NUM_DCCPROTO 6 struct dccproto {