I agree, I have no idea how the internals of PF work, and what it is capable of, which is why I emailed the list :)
Would it be possible or feasible to write a daemon that does perform logic and state tracking w/ regex, that could possibly speak to the PF internals to add rules dynamically? Then again, I am slow, because I just realized I could just use SNORT and its Plugins to dynamically add rulesets. Forgive my ignorance, I am going to now go shoot myself.
regards,
chris
Daniel Hartmeier wrote:
On Mon, Jun 28, 2004 at 03:16:15PM -0700, cloper wrote:
drop in log quick on $ext_if layer 7 "edonkey login"
drop in log quick on $ext_if layer 7 "aim send message"
While for applications TCP is a byte stream protocol, packet filters (as the name implies) operate on a lower level. They do not see payload as an ordered stream, but see individual packets containing chunks of payload. In general, the payload is broken into chunks at arbitrary borders, chunks can arrive in non-sequential order and there can be gaps and overlaps.
So, if you look at these individual packets as they pass through, you can't really 'look for the string "edonkey"' inside the stream. All you could do is look for the string inside one packet's payload.
But that's generally useless, as
a) the string could be broken into multiple packets by chance legitimately (not very likely, but it will occur)
b) the sender can intentionally break the string into multiple packets to bypass the filter (very likely and very easy to do), send chunks out of order or create overlaps.
c) there will be many unexpected false positives. If you had the above rules in place, you wouldn't have been able to send your email, would you? If you'd browse the lists archive over HTTP, you couldn't load the page containing this reply. There's an endless list of protocols that might legally contain these strings, and you can't rely on P2P protocols to not use arbitrary ports.
If you'd be happy with that, this would be easy to hack into pf. Just add a char array to struct pf_rule, make pfctl parse the search arguments into rules, and have the kernel search for the arguments inside each packet. But given a)-c), it will prove useless, IMO. A P2P client can cause a string to get split up using one or two lines of simple userland socket option fiddling. I wouldn't be surprised if some of them already do that just for this purpose. If they don't, their next releases will for sure. Either you have a solid reliable solution, or you write code that will be useless in two months when the opponents catch up.
So, you'll end up wanting to reassemble packets into streams before searching for strings. And you'll end up adding protocol awareness to the code (so a HTML document containing the sequence "edonkey login" sent over HTTP doesn't match). Restricting rules to ports won't help much, as P2P protocols intentionally use port 80 and HTTP-alike protocols exactly to get through firewalls and proxies more easily.
You'll find that a single simple piece of code won't do, instead you'll need to implement stateful logic for all protocols you don't want to block outright.
Once you're there, you'll find that doing this in kernel makes it
a) harder (writing your own TCP reassembly in kernel is certainly more complex than just getting a stream in userland). b) more dangerous (mistakes easily produce remotely exploitable crashes or kernel compromises)
compared to doing the same in userland as application level proxies.
The most simple and reliable solution, in the end, would be to pick existing proxies for the protocols you need to forward (like squid for HTTP) and add the filtering there, if they do not already support such features.
I think the idea of layer 7 filtering in packet filters is popular because it appears to make things simple (just block "edonkey login" in the packet filter and be done, no need to think about different protocols etc.), but it's just flawed. Feel free to prove me wrong, but I won't spend time writing code just to prove the point that the idea doesn't work. :)
Daniel