- 论坛徽章:
- 0
|
- /*
- linux-2.6.22.5/net/netfilter/nf_conntrack_proto_tcp.c
- 2007-12-22 10:38
- ¥6
- howto crash netfilter with TCP SYN packet, possible?
- */
- static int tcp_error(struct sk_buff *skb,
- unsigned int dataoff,
- enum ip_conntrack_info *ctinfo,
- int pf,
- unsigned int hooknum)
- {
- struct tcphdr _tcph, *th;
- unsigned int tcplen = skb->len - dataoff;
- u_int8_t tcpflags;
- /* Smaller that minimal TCP header? */
- th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
- if (th == NULL) {
- if (LOG_INVALID(IPPROTO_TCP))
- nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
- "nf_ct_tcp: short packet ");
- return -NF_ACCEPT;
- }
- /* Not whole TCP header or malformed packet */
- if (th->doff*4 < sizeof(struct tcphdr)
- || tcplen < th->doff*4) {
- [color=Red] /*
- * 其一 隐患;/
- * TCP头长大于60字节是合法的 8-(:
- */
- [/color] if (LOG_INVALID(IPPROTO_TCP))
- nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
- "nf_ct_tcp: truncated/malformed packet ");
- return -NF_ACCEPT;
- }
- /* Checksum invalid? Ignore.
- * We skip checking packets on the outgoing path
- * because the checksum is assumed to be correct.
- */
- /* FIXME: Source route IP option packets --RR */
- if (nf_conntrack_checksum &&
- ((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
- (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) &&
- nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {
- if (LOG_INVALID(IPPROTO_TCP))
- nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
- "nf_ct_tcp: bad TCP checksum ");
- return -NF_ACCEPT;
- }
- /* Check TCP flags. */
- tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH));
- if (!tcp_valid_flags[tcpflags]) {
- if (LOG_INVALID(IPPROTO_TCP))
- nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
- "nf_ct_tcp: invalid TCP flag combination ");
- return -NF_ACCEPT;
- }
- return NF_ACCEPT;
- }
- static void tcp_options(const struct sk_buff *skb,
- unsigned int dataoff,
- struct tcphdr *tcph,
- struct ip_ct_tcp_state *state)
- {
- unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
- unsigned char *ptr;
- int length = (tcph->doff*4) - sizeof(struct tcphdr);
- /*
- * length可以大于40;/
- * but sizeof(buff) = 40
- */
- if (!length)
- return;
- ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
- length, buff);
- [color=Red] /*
- * 其二 buff可能overflow
- */
- [/color] BUG_ON(ptr == NULL);
- state->td_scale = state->flags = 0;
- while (length > 0) {
- int opcode = *ptr++;
- int opsize;
- switch (opcode) {
- case TCPOPT_EOL:
- return;
-
- case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
- length--;
- continue;
-
- default:
- opsize = *ptr++;
-
- if (opsize < 2) /* "silly options" */
- return;
-
- if (opsize > length)
- break; /* don't parse partial options */
- if (opcode == TCPOPT_SACK_PERM
- && opsize == TCPOLEN_SACK_PERM) {
- state->flags |= IP_CT_TCP_FLAG_SACK_PERM;
- }
- else if (opcode == TCPOPT_WINDOW
- && opsize == TCPOLEN_WINDOW) {
- state->td_scale = *(u_int8_t *)ptr;
- if (state->td_scale > 14) {
- /* See RFC1323 */
- state->td_scale = 14;
- }
- state->flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
- }
- ptr += opsize - 2;
- length -= opsize;
- }
- }
- }
- static inline void * skb_header_pointer (const struct sk_buff *skb,
- int offset, int len, void *buffer)
- { /* in <linux/skbuff.h> */
-
- int hlen = skb_headlen(skb);
- if (hlen - offset >= len)
- return skb->data + offset;
- [color=Red] /*
- * 其三 如果是大SYN包,使得skb_copy_bits被调用了,
- * if (len > 40 bytes)
- * buffer overflow;
- *
- * 但是,netfilter看到的包都是整合过的,
- * 这样的机会几乎没有,要看ipfrag reasm的结果
- */
- [/color] if (skb_copy_bits(skb, offset, buffer, len) < 0)
- return NULL;
- return buffer;
- }
- int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
- {
- int i, copy;
- int start = skb_headlen(skb);
- if (offset > (int)skb->len - len)
- goto fault;
- /* Copy header. */
- if ((copy = start - offset) > 0) {
- if (copy > len)
- copy = len;
- /*
- 这里没有check len,
- 而是默认len <= sizeof(to)
- */
- skb_copy_from_linear_data_offset(skb, offset, to, copy);
- if ((len -= copy) == 0)
- return 0;
- offset += copy;
- to += copy;
- }
- ...
- }
- void * memcpy(void *dest, const void *src, size_t count)
- {
- /*
- C版的memcpy in lib/string.c
- memcpy没有检测overflow的义务;/
- */
- char *tmp = dest;
- const char *s = src;
- while (count--)
- *tmp++ = *s++;
- return dest;
- }
- static struct sk_buff * ip_frag_reasm (struct ipq *qp, struct net_device *dev)
- { /* net/ipv4/ip_fragment.c */
-
- struct iphdr *iph;
- struct sk_buff *fp, *head = qp->fragments;
- int len;
- int ihlen;
- ipq_kill(qp);
- BUG_TRAP(head != NULL);
- BUG_TRAP(FRAG_CB(head)->offset == 0);
- /* Allocate a new buffer for the datagram. */
- ihlen = ip_hdrlen(head);
- len = ihlen + qp->len;
- if (len > 65535)
- goto out_oversize;
- /* Head of list must not be cloned. */
- if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
- goto out_nomem;
- /* If the first fragment is fragmented itself, we split
- * it to two chunks: the first with data and paged part
- * and the second, holding only fragments. */
- if (skb_shinfo(head)->frag_list) {
- struct sk_buff *clone;
- int i, plen = 0;
- if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
- goto out_nomem;
- clone->next = head->next;
- head->next = clone;
- skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
- skb_shinfo(head)->frag_list = NULL;
- for (i=0; i<skb_shinfo(head)->nr_frags; i++)
- plen += skb_shinfo(head)->frags[i].size;
- clone->len = clone->data_len = head->data_len - plen;
- head->data_len -= clone->len;
- head->len -= clone->len;
- clone->csum = 0;
- clone->ip_summed = head->ip_summed;
- atomic_add(clone->truesize, &ip_frag_mem);
- }
- skb_shinfo(head)->frag_list = head->next;
- [color=Red] /*
- * 其四 linux skb精巧的设计,可以方便地处理大包 &)
- *
- * 在这里没有实施linearize,性能可佳。
- */
- [/color] skb_push(head, head->data - skb_network_header(head));
- atomic_sub(head->truesize, &ip_frag_mem);
- for (fp = head->next; fp; fp = fp->next) {
- head->data_len += fp->len;
- [color=Red] /*
- * 由于没有linearize操作,
- * 虽然总包长要求len < 65535 byte,
- * 首包长确可以是
- * 82byte = 20byte<IP> + 60byte<TCP> + 2byte<trash>
- *
- * 所以 tcp->doff*4 = 88byte,
- * 将迫使skb_header_pointer调用skb_copy_bits,
- * 结果导致40byte的buff overflow
- */
- [/color] head->len += fp->len;
-
- if (head->ip_summed != fp->ip_summed)
- head->ip_summed = CHECKSUM_NONE;
- else if (head->ip_summed == CHECKSUM_COMPLETE)
- head->csum = csum_add(head->csum, fp->csum);
-
- head->truesize += fp->truesize;
- atomic_sub(fp->truesize, &ip_frag_mem);
- }
- head->next = NULL;
- head->dev = dev;
- head->tstamp = qp->stamp;
- iph = ip_hdr(head);
- iph->frag_off = 0;
- iph->tot_len = htons(len);
- IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
- qp->fragments = NULL;
- return head;
- out_nomem:
- LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
- "queue %p\n", qp);
- goto out_fail;
- out_oversize:
- if (net_ratelimit())
- printk(KERN_INFO
- "Oversized IP packet from %d.%d.%d.%d.\n",
- NIPQUAD(qp->saddr));
- out_fail:
- IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
- return NULL;
- }
复制代码
[ 本帖最后由 sisi8408 于 2007-12-22 14:42 编辑 ] |
|