Re: [zorp] Stacking programs doesn’t work and how to modify POST parameters?
Hi, Thanks a lot for your quick reply! I thought that Zorp 3.3 already exists because I saw press releases for the commercial version and some files in the download section named with 3.3. But ok, then it's clear why it would not install ;) I would be glad for any code or tutorial that could help me in checking POST data and controlling the response. Performance is not the problem because it's just for a very small amount of users and running on a dedicated server. I saw you've written something about AnyPy-proxy here (seems to be also a scenario with POST data) https://lists.balabit.hu/pipermail/zorp-hu/2007-May/002248.html but unfortunately I don't understand a word and online translator programs make it even less understandable ;) Generally, I would prefer the program stacking for checking POST data and responses through an external program instead of using custom python code. Do you know how long it will approximately take until the program stacking feature is released under GPL? I don't want to rush you and I'm very glad and thankful that Zorp is released in a GPL version and you're investing your valuable time in adding features to the GPL version. Unfortunately I've only a limited time frame until end of July to get this running and so I would need to look for a different temporary solution (either through AnyPy or through Squid, Snort_inline or so...) if it's released later and come back to Zorp when this feature is available... Thomas Wenz -- Psssst! Schon vom neuen GMX MultiMessenger gehört? Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger
On Mon, 2008-06-30 at 17:55 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Thanks a lot for your quick reply! I thought that Zorp 3.3 already exists because I saw press releases for the commercial version and some files in the download section named with 3.3. But ok, then it's clear why it would not install ;)
I would be glad for any code or tutorial that could help me in checking POST data and controlling the response. Performance is not the problem because it's just for a very small amount of users and running on a dedicated server.
I saw you've written something about AnyPy-proxy here (seems to be also a scenario with POST data) https://lists.balabit.hu/pipermail/zorp-hu/2007-May/002248.html but unfortunately I don't understand a word and online translator programs make it even less understandable ;)
Generally, I would prefer the program stacking for checking POST data and responses through an external program instead of using custom python code. Do you know how long it will approximately take until the program stacking feature is released under GPL? I don't want to rush you and I'm very glad and thankful that Zorp is released in a GPL version and you're investing your valuable time in adding features to the GPL version. Unfortunately I've only a limited time frame until end of July to get this running and so I would need to look for a different temporary solution (either through AnyPy or through Squid, Snort_inline or so...) if it's released later and come back to Zorp when this feature is available...
I can do a non-official release of our 3.3 tree with this feature added, probably tonight. -- Bazsi
I can do a non-official release of our 3.3 tree with this feature added, probably tonight.
-- Bazsi
This sounds really great. Thanks in advance! Thomas Wenz -- Psssst! Schon das coole Video vom GMX MultiMessenger gesehen? Der Eine für Alle: http://www.gmx.net/de/go/messenger03
On Wed, 2008-07-02 at 15:14 +0200, thomas.wenz@gmx-topmail.de wrote:
I can do a non-official release of our 3.3 tree with this feature added, probably tonight.
-- Bazsi
This sounds really great. Thanks in advance!
Thomas Wenz
This is about the closest thing I can do now as a release. http://people.balabit.hu/bazsi/zorp-gpl-3.3/ This actually compiled for me, but the reason might be that my build environment has something installed. I did not try to run it, so you might get missing files here and there. Just let me know if you do. You need libzorpll 3.3.0.3 in order to compile it, but I've also uploaded it to the same location. -- Bazsi
Hi, Thanks for the release. Unfortunately, it doesn't work yet: The library compiles fine but Zorp itself not. I've attached the error below. It seems like zorp/misc/zcp.h is missing (which is somewhat clear because the ZCP isn't part of GPL version as far as I know). I then removed the "#include <zorp/misc/zcp.h>" and "ZCPContext *control_proto;" from the proxystack.h but I'm unsure if this could lead to problems because I'm changing the ZStackedProxy struct? Without these two lines, compilation works. I installed it with this change and upon starting Zorp I get the following error: =========================================== Starting Zorp Firewall Suite: Traceback (most recent call last): File "/home/fw/conf/http.py", line 1, in ? from Zorp.Core import * File "/usr/share/zorp/pylib/Zorp/Core.py", line 38, in ? from Zone import InetZone File "/usr/share/zorp/pylib/Zorp/Zone.py", line 135, in ? import kznf.kznfnetlink ImportError: No module named kznf.kznfnetlink =========================================== This import is used in multiple python-files of Zorp (Dispatch, KZorp, NAT, Service, Zone). It seems to be not an official library as I couldn't find anything about it. I couldn't figure out how to solve this... Thomas Wenz gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/home/compile/zorp/zorp-3.3.1a+20080704+0000/lib -I/home/compile/zorp/zorp-3.3.1a+20080704+0000 -D_GNU_SOURCE -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/python2.4 -O2 -Wall -W -Werror-implicit-function-declaration -g -D_GNU_SOURCE -MT proxystack.lo -MD -MP -MF .deps/proxystack.Tpo -c proxystack.c -fPIC -DPIC -o .libs/proxystack.o In file included from proxystack.c:30: ./zorp/proxystack.h:32:27: error: zorp/misc/zcp.h: File or directory not found In file included from proxystack.c:30: ./zorp/proxystack.h:50: error: expected specifier-qualifier-list before âZCPContextâ proxystack.c: In function âz_proxy_stack_tupleâ: proxystack.c:267: warning: unused variable âprovider_nameâ proxystack.c:267: warning: unused variable âstack_infoâ proxystack.c:266: warning: unused variable âsaâ proxystack.c: In function âz_stacked_proxy_newâ: proxystack.c:390: error: âZStackedProxyâ has no member named âproxyâ proxystack.c:392: error: âZStackedProxyâ has no member named âchild_proxyâ proxystack.c: In function âz_stacked_proxy_destroyâ: proxystack.c:438: error: âZStackedProxyâ has no member named âchild_proxyâ proxystack.c:440: error: âZStackedProxyâ has no member named âproxyâ proxystack.c:440: error: âZStackedProxyâ has no member named âchild_proxyâ proxystack.c:441: error: âZStackedProxyâ has no member named âchild_proxyâ proxystack.c:442: error: âZStackedProxyâ has no member named âchild_proxyâ proxystack.c:444: error: âZStackedProxyâ has no member named âproxyâ proxystack.c:446: error: âZStackedProxyâ has no member named âproxyâ proxystack.c:447: error: âZStackedProxyâ has no member named âproxyâ make[4]: *** [proxystack.lo] Error 1 make[4]: Leaving directory `/home/compile/zorp/zorp-3.3.1a+20080704+0000/lib' make[3]: *** [all-recursive] Error 1 make[3]: Leaving directory `/home/compile/zorp/zorp-3.3.1a+20080704+0000/lib' make[2]: *** [all-recursive] Error 1 make[2]: Leaving directory `/home/compile/zorp/zorp-3.3.1a+20080704+0000' make[1]: *** [all] Error 2 make[1]: Leaving directory `/home/compile/zorp/zorp-3.3.1a+20080704+0000' make: *** [build-stamp] Error 2 -- Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
On Fri, 2008-07-04 at 01:47 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Thanks for the release. Unfortunately, it doesn't work yet:
The library compiles fine but Zorp itself not. I've attached the error below. It seems like zorp/misc/zcp.h is missing (which is somewhat clear because the ZCP isn't part of GPL version as far as I know). I then removed the "#include <zorp/misc/zcp.h>" and "ZCPContext *control_proto;" from the proxystack.h but I'm unsure if this could lead to problems because I'm changing the ZStackedProxy struct? Without these two lines, compilation works.
Yes, I probably had a library installed in the build chroot that had those. I intended to release zcp as well (zcp stands for zorp core protocol and is used between several zorp components, like the auth/content vectoring servers), but that sits in another library that is not GPLed and the complete library cannot be released as GPL. So for now I disabled the "control" channel of the stacked programs, but I've left these references there. This control channel is used to propagate verdicts from the stacked program to the proxy, e.g. it can tell the proxy to reject the contents with a specific error message describing the situation. I've uploaded zorp_3.3.1a+20080704+0753.tar.gz to the same location, it now should build correctly.
I installed it with this change and upon starting Zorp I get the following error: =========================================== Starting Zorp Firewall Suite: Traceback (most recent call last): File "/home/fw/conf/http.py", line 1, in ? from Zorp.Core import * File "/usr/share/zorp/pylib/Zorp/Core.py", line 38, in ? from Zone import InetZone File "/usr/share/zorp/pylib/Zorp/Zone.py", line 135, in ? import kznf.kznfnetlink ImportError: No module named kznf.kznfnetlink =========================================== This import is used in multiple python-files of Zorp (Dispatch, KZorp, NAT, Service, Zone). It seems to be not an official library as I couldn't find anything about it. I couldn't figure out how to solve this...
Hmm... I think I did release this part, it is located under pylib/kznf/kznf directory in the source tree. It should be installed you invoke make install. It is also compiled into a python-kzorp package if you build a .deb. These files are used to communicate to the in-kernel part of Zorp, named KZorp. I'm not sure whether you also want to use the KZorp component, Zorp will work without it and properly configuring it can be quiet involved. Without KZorp, Zorp will display an error during startup, but then operate correctly afterwards. http://www.balabit.com/downloads/files/kernel-patches/2.6.17-kzorp/ -- Bazsi
Hi, Perhaps it was just too late for me ;) The python-kzorp package was there but I didn't notice it and dpkg didn't complain about it (it's not a dependancy of zorp). Sorry! As you predicted I get some startup errors complaining that it can't flush KZorp config. If I understand correct, KZorp is a faster solution and allows including iptables/NAT rules into Zorp. Are there any other advantages I missed? However, It's not yet working because it somehow gets wrong IPs with my unchanged config form the old Zorp(dest is wrong, remote and local are correct): core.debug(6): (dsp/dispatch:0): Incoming connection; protocol='1', remote='AF_INET(10.1.1.1:1704)', local='AF_INET(10.1.1.251:50080)', dest='AF_INET(10.1.1.251:50080)' It then fetches the GET request and tries to connect to itself (which fails). However, I did not notice the self.request_stack["GET"] entry being performed. Is this done after the connection has been established? If so, I think this would be a little bit late...in my opinion it should be before header filtering or earlier in order to be able to adjust the content length header (can't be adjusted through the stacked program as far as I read), log the umodified request and to prevent connecting in case there's something evil in the data part. Do I perhaps need a new kernel with the new TProxy 4.0 as there's a line core.debug(6): (nosession): System dependant init; sysdep_tproxy='tproxy40' If so, should a leave KZorp out of the kernel because if I compile it in, I need to configure it, correct? Thomas Wenz -- GMX startet ShortView.de. Hier findest Du Leute mit Deinen Interessen! Jetzt dabei sein: http://www.shortview.de/wasistshortview.php?mc=sv_ext_mf@gmx
On Fri, 2008-07-04 at 12:53 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Perhaps it was just too late for me ;) The python-kzorp package was there but I didn't notice it and dpkg didn't complain about it (it's not a dependancy of zorp). Sorry!
As you predicted I get some startup errors complaining that it can't flush KZorp config. If I understand correct, KZorp is a faster solution and allows including iptables/NAT rules into Zorp. Are there any other advantages I missed?
Not really. Some of the access control is pushed down into kernel space, so that you can use the same access control model for forwarded traffic as for proxied traffic. Also, you can use zones inside your packet filter rules.
However, It's not yet working because it somehow gets wrong IPs with my unchanged config form the old Zorp(dest is wrong, remote and local are correct): core.debug(6): (dsp/dispatch:0): Incoming connection; protocol='1', remote='AF_INET(10.1.1.1:1704)', local='AF_INET(10.1.1.251:50080)', dest='AF_INET(10.1.1.251:50080)' It then fetches the GET request and tries to connect to itself (which fails).
That's bad, Zorp could not find out the original target address. Where did you get your kernel from? I mean which tproxy version are you using? The one destined for kernel inclusion is dubbed version 4.1, but it is completely incompatible with what we currently have in our ZorpOS kernel, which is dubbed version 4.0.
However, I did not notice the self.request_stack["GET"] entry being performed. Is this done after the connection has been established? If so, I think this would be a little bit late...in my opinion it should be before header filtering or earlier in order to be able to adjust the content length header (can't be adjusted through the stacked program as far as I read), log the umodified request and to prevent connecting in case there's something evil in the data part.
The "GET" request has no data payload, that's why it is not stacking anything. For POST it should start the stacked program before connecting to the destination.
Do I perhaps need a new kernel with the new TProxy 4.0 as there's a line core.debug(6): (nosession): System dependant init; sysdep_tproxy='tproxy40' If so, should a leave KZorp out of the kernel because if I compile it in, I need to configure it, correct?
From Zorp's point of view it does not matter whether you use tproxy 4.0 or tproxy 4.1, it should work with both.
How do you redirect traffic to Zorp? Are you using TPROXY target or REDIRECT (in the nat table?) If the latter then I understand why it does not detect the original target address. -- Bazsi
Hi, I was using the kernel from the latest ZorpOS which worked for 3.14. I've compiled a new kernel with kernel-patchtree-2.6.17-zorpos-4.1.4. This loads up TProxy 4.0 and now it works! The old kernel had TProxy 3.0 so this was the problem.
The "GET" request has no data payload, that's why it is not stacking anything. For POST it should start the stacked program before connecting to the destination.
From my limited point of view, it looks like it's after the connect (see log below). Do you know a possibility how to move it up so that it's called before "Filtering request and headers;"? Or can I alternatively perform own changes in the "Request postfilter header;"-section (meaning: moving the "Filtering request and headers;" section down)? Is there also a possibility to activate it for GET and emtpy requests(in fact, if a tamper a POST so that it contains no payload it's also not handled over)? I think I need to remove some checks in the C-code to achieve this, correct?
I actually need the aboce for the following scenario: 1. The whole request is handled over to an external program no matter what it contains. (I first tried it with an AnyPy in front but I couldn't stack http on it...) 2. The external program decides what needs to be changed (headers and data) and logs the whole requests. 3. I've already managed to include some code in http.c so that a python-function is called just before headers are modified through Zorp. Based on the result of the external program I know how to change the headers (not in the config-function like normally where I have no information about the request). The link between the external program and the python function is done by asking for an ID in a database which returns the changes to be made. This also makes it possible to filter headers which are unknown before the request arrives. It's not really a performance solution but the normal rules are a little bit too static for me and security is considered higher as performance for me... I surely could do that by running some kind of tcpdump but this doesn't work with SSL and it's difficult to synchronize (Zorp could have already sent before the changes are calculated!). Thomas Wenz http.debug(6): (svc/HTTP_instance:0/http): processing request and headers; http.debug(6): (svc/HTTP_instance:0/http): Filtering request and headers; http.debug(6): (svc/HTTP_instance:0/http): Reprocessing filtered request; http.accounting(4): (svc/HTTP_instance:0/http): Accounting; command='POST', url='http://10.1.1.9/test.php' http.debug(6): (svc/HTTP_instance:0/http): Sending request and headers, copying request data; core.debug(6): (svc/HTTP_instance:0/http): Attribute fetched; attribute='server_local_tos', value='0' core.debug(7): (svc/HTTP_instance:0/http): Connecting to remote host; protocol='1', local='AF_INET(10.1.1.1:4345)', remote='AF_INET(10.1.1.9:80)' core.debug(7): (svc/HTTP_instance:0/http): Initiating connection; from='AF_INET(10.1.1.1:4345)', to='AF_INET(10.1.1.9:80)' core.debug(6): (svc/HTTP_instance:0/http): Established connection; protocol='1', remote='AF_INET(10.1.1.9:80)', local='AF_INET(10.1.1.1:4345)', dest='AF_INET(10.1.1.9:80)' core.session(3): (svc/HTTP_instance:0/http): Server connection established; server_fd='18', server_address='AF_INET(10.1.1.9:80)', server_zone='Zone(attack, 10.1.1.0/24)', server_local='AF_INET(10.1.1.1:4345)', server_protocol='TCP' core.debug(6): (svc/HTTP_instance:0/http): Attribute fetched; attribute='request_method', value=''POST'' core.debug(6): (svc/HTTP_instance:0/http): Stacking program; client='19:20', server='21:22', control='23:24', program='/bin/echo' core.debug(6): (svc/HTTP_instance:0/http/client_downstream): Shutdown channel; fd='19', mode='0' core.debug(6): (svc/HTTP_instance:0/http/server_downstream): Shutdown channel; fd='21', mode='1' core.debug(7): (svc/HTTP_instance:0/http): Eofmask is updated; old_mask='0000', eof_mask='0000' core.debug(7): (svc/HTTP_instance:0/http): Eofmask is updated; old_mask='0000', eof_mask='0000' core.dump(7): (svc/HTTP_instance:0/http/client): Reading stream; stream='ZStreamLine', count='12' core.dump(9): (svc/HTTP_instance:0/http/client): data line 0x0000: 74 65 73 74 3D 64 64 66 67 64 66 67 test=ddfgdfg core.dump(7): (svc/HTTP_instance:0/http/client): Reading stream; stream='ZStreamLine', count='12' core.dump(9): (svc/HTTP_instance:0/http/client): data line 0x0000: 74 65 73 74 3D 64 64 66 67 64 66 67 test=ddfgdfg core.debug(7): (svc/HTTP_instance:0/http): Eofmask is updated; old_mask='0000', eof_mask='0400' core.dump(7): (svc/HTTP_instance:0/http/client_downstream): Writing stream; stream='ZStreamFD', count='12' core.dump(9): (svc/HTTP_instance:0/http/client_downstream): data line 0x0000: 74 65 73 74 3D 64 64 66 67 64 66 67 test=ddfgdfg core.debug(6): (svc/HTTP_instance:0/http/client_downstream): Shutdown channel; fd='19', mode='1' core.debug(7): (svc/HTTP_instance:0/http): Eofmask is updated; old_mask='0400', eof_mask='0500' core.dump(7): (svc/HTTP_instance:0/http/server_downstream): Reading stream; stream='ZStreamFD', count='12' core.dump(9): (svc/HTTP_instance:0/http/server_downstream): data line 0x0000: 74 65 73 74 3D 64 64 66 67 64 66 67 test=ddfgdfg http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Host', value='10.1.1.9' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='User-Agent', value='Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9) Gecko/2008052906 Firefox/3.0' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Accept', value='text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Accept-Language', value='de-de,de;q=0.8,en-us;q=0.5,en;q=0.3' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Accept-Encoding', value='gzip,deflate' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Accept-Charset', value='ISO-8859-1,utf-8;q=0.7,*;q=0.7' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Keep-Alive', value='300' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Connection', value='keep-alive' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Referer', value='http://10.1.1.9/test.php' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Content-Type', value='application/x-www-form-urlencoded' http.request(7): (svc/HTTP_instance:0/http): Request postfilter header; hdr='Transfer-Encoding', value='chunked' -- Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
On Fri, 2008-07-04 at 19:58 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
I was using the kernel from the latest ZorpOS which worked for 3.14. I've compiled a new kernel with kernel-patchtree-2.6.17-zorpos-4.1.4. This loads up TProxy 4.0 and now it works! The old kernel had TProxy 3.0 so this was the problem.
Great. Zorp 3.3 should also support the old tproxy, but probably you'd have to override the default using the "--tproxy tproxy30" command line option. But tproxy4 is better anyway. And as a plus you got kzorp as well.
The "GET" request has no data payload, that's why it is not stacking anything. For POST it should start the stacked program before connecting to the destination.
From my limited point of view, it looks like it's after the connect (see log below). Do you know a possibility how to move it up so that it's called before "Filtering request and headers;"? Or can I alternatively perform own changes in the "Request postfilter header;"-section (meaning: moving the "Filtering request and headers;" section down)?
You are right indeed. The server side connection is established right before the stacking starts. The reason is that by default the POST data is not buffered in memory, it is copied in stream mode. E.g. whenever a new POST packet comes in, it is copied to the stacked proxy straight, which then copies it to the server without keeping it in a buffer. (POST data can be quite large) It should be possible to delay the connection even further by moving the http_connect_server() call from http_copy_request() to http_transfer_dst_write_preamble(). Something like: http_transfer_dst_write_preamble(HttpTransfer *self, ZStream *stream, GError **err) { GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize bw; if (self->preamble_ofs == 0) { if (!http_connect_server(self)) { g_set_error(err); return G_IO_STATUS_ERROR; } http_log_headers((HttpProxy *) self->super.owner, self->transfer_from, "postfilter"); } The only problem is that the data transfer subsystem assumes that the server side stream already exists when it is started. It is not impossible to lift this assumption though. (this can be seen in libproxy/transfer.c, z_transfer2_start, it binds callbacks to various I/O events). Hmm, hmm.. Now as I think of it, there's another possibility, which is somewhat simpler. The HTTP proxy is capable of reading the entire POST data before connecting to the server at all and storing it in a blob (more about blobs later). In this case the entire request together with the POST payload is fetched into memory, and after filtering it is sent to the server. This code path is activated if you set "self.rerequest_attempts" to something greater than zero. So if you set rerequest_attempts, your stacked proxy will start earlier than the server side connection is established. So the promised explanation of blobs. A blob is a store for potentially large chunks of data with administrator controlled memory and disk use limits. When memory is still available, this data is stored in memory. When memory constraints become tight, data is swapped out into files. Ok, then you'll need to enable stacking for GET requests. The function that decides whether to stack a proxy lives here: http_transfer_stack_proxy(), and here's the condition you are looking for: /* we don't stack anything, if 1) we suppress data 2) data is not indicated by either the presence of some header fields nor do we expect data */ if (self->suppress_data || !(self->expect_data || http_lookup_header(headers, "Transfer-Encoding", &hdr) || http_lookup_header(headers, "Content-Length", &hdr))) { *stacked = NULL; return TRUE; } In the case of "GET", suppress_data is FALSE, expect_data is FALSE and there are no "Transfer-Encoding" or "Content-Length" headers in the input. As you want to send headers to the downstream proxy anyway, you enable "self.push_mime_headers", so if you change the above condition to: if (self->suppress_data || !(self->expect_data || self->push_mime_headers || http_lookup_header(headers, "Transfer-Encoding", &hdr) || http_lookup_header(headers, "Content-Length", &hdr))) ^^^^^^^^^^^^^^^^^^^^^^^ This will trigger stacking and start your program as a child proxy, which also receives the MIME headers (e.g. the HTTP headers with Content-Length/Transfer-Encoding/Connection removed)
Is there also a possibility to activate it for GET and emtpy requests(in fact, if a tamper a POST so that it contains no payload it's also not handled over)? I think I need to remove some checks in the C-code to achieve this, correct?
A proxy is stacked if there are headers to indicate that there's data. The stacking feature was meant to be used on HTTP payload on on HTTP requests/responses.
I actually need the aboce for the following scenario: 1. The whole request is handled over to an external program no matter what it contains. (I first tried it with an AnyPy in front but I couldn't stack http on it...)
Hmm.. you can side-stack HTTP beside an AnyPy, like this: client -> AnyPy -> Http -> server You cannot stack HTTP _into_ AnyPy.
2. The external program decides what needs to be changed (headers and data) and logs the whole requests.
Hmm.. stacking was meant to be used for data inspection (virus & spam checking) in the case of HTTP. Although the headers can be sent to the child proxy (using the push_mime_headers option), if they are changed those are not sent to the server. Do you really want to change headers? If you do, isn't the power of the Python binding enough? E.g. def config(self): .... self.request["GET"] = (HTTP_REQ_POLICY, self.checkHeaders) def checkHeaders(self, method, url, version): if self.getRequestHeader("X-Interesting-Http-Header"): self.setRequestHeader("X-Interesting-Http-Header", "new-value") It might be difficult to change the request headers based on the data, but otherwise I can hardly think of any manipulation that couldn't be done by the stock HTTP proxy.
3. I've already managed to include some code in http.c so that a python-function is called just before headers are modified through Zorp. Based on the result of the external program I know how to change the headers (not in the config-function like normally where I have no information about the request). The link between the external program and the python function is done by asking for an ID in a database which returns the changes to be made. This also makes it possible to filter headers which are unknown before the request arrives. It's not really a performance solution but the normal rules are a little bit too static for me and security is considered higher as performance for me...
There are too many options, let me see what you think about the contents of this email, and then I can probably recomment you a good/better solution.
I surely could do that by running some kind of tcpdump but this doesn't work with SSL and it's difficult to synchronize (Zorp could have already sent before the changes are calculated!).
-- Bazsi
Hi, Thanks for taking you so much time to help me! Your idea with self.rerequest_attempts setting to 1 is fantastic! I tried it and it moves the stacking so much up like I want it! Now I can do exactly what I described with POST! The problem left is that stacking is still not done on GET-requests. Somehow, your suggested change didn't do anything because it's an OR-clause and if any of these checks fails it enters the section and ends. If I put a comment around this whole if-section, it's a little bit better: the program gets executed but a) it's then not executed at the same time like with POST and self.rerequest_attempts=1 so it seems like the alternative path with blobs can only be used with POST. I tried changing the has_data value in the http_fetch_request function but it didn't help (probably there's more needed). b) it's then executed but still no data (= no headers) is passed so it's quite useless. There must be another check somewhere later in the program which prevents sending the headers when there's no data part. I just noticed another minor problem: A connection takes quite long if I set forge_port to Z_PORT_EXACT or Z_PORT_GROUP. Between the "Initiating connection" and "Established connection" it takes more than 3 seconds. If I set "self.transparent_mode = TRUE" this gets really slow because then the connection seems to be not reused, if it's set to false it goes really fast (only the first request and if there was no request for some time take the 3 seconds). Why is this so? Does it wait for the other connection to somehow time out before it can use the same port? With Z_PORT_ANY it works flawlessly... And a more cosmetical issue I found during the investigation of the connection problem: When I attach an strace to a process with a stacked program I get a huge filte (18MB) with thousands of "close(229284) = -1 EBADF (Bad file descriptor)" At one point this process then calls the stacked proxy and seems to normally continue. Is this normal? I've attached the log of this process below. Thomas Wenz 7415 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb6de3bf8) = 7416 7416 dup2(19, 0 <unfinished ...> 7416 <... dup2 resumed> ) = 0 7416 dup2(21, 1 <unfinished ...> 7416 <... dup2 resumed> ) = 1 7416 dup2(23, 3 <unfinished ...> 7416 <... dup2 resumed> ) = 3 7416 getrlimit(RLIMIT_NOFILE, <unfinished ...> 7416 <... getrlimit resumed> {rlim_cur=250*1024, rlim_max=250*1024}) = 0 7416 close(4 <unfinished ...> 7416 <... close resumed> ) = 0 7416 close(5 <unfinished ...> 7416 <... close resumed> ) = 0 ... 7416 <... close resumed> ) = 0 7416 close(24 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) 7416 close(25 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) 7416 close(26 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) .... 7416 close(255999) = -1 EBADF (Bad file descriptor) 7416 execve("/bin/sh", ["/bin/sh", "-c", "/usr/bin/cat"], [/* 15 vars */]) = 0 -- GMX startet ShortView.de. Hier findest Du Leute mit Deinen Interessen! Jetzt dabei sein: http://www.shortview.de/wasistshortview.php?mc=sv_ext_mf@gmx
2008/7/7 <thomas.wenz@gmx-topmail.de>:
The problem left is that stacking is still not done on GET-requests.
Because GET requests do not have body, and when there is no body (or it is supressed), there is no stacking. It would not be meaningful: everything outside the body can be modified by the configuration of the proxy. Soon I will upload the FormProxy code and the related tutorials somewhere, and post the URL here.
On Mon, 2008-07-07 at 15:57 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Thanks for taking you so much time to help me!
You are welcome. I like doing my best for people who see the potential behind Zorp.
Your idea with self.rerequest_attempts setting to 1 is fantastic! I tried it and it moves the stacking so much up like I want it! Now I can do exactly what I described with POST!
The problem left is that stacking is still not done on GET-requests. Somehow, your suggested change didn't do anything because it's an OR-clause and if any of these checks fails it enters the section and ends. If I put a comment around this whole if-section, it's a little bit better: the program gets executed but a) it's then not executed at the same time like with POST and self.rerequest_attempts=1 so it seems like the alternative path with blobs can only be used with POST. I tried changing the has_data value in the http_fetch_request function but it didn't help (probably there's more needed). b) it's then executed but still no data (= no headers) is passed so it's quite useless. There must be another check somewhere later in the program which prevents sending the headers when there's no data part.
There's a patch below, which makes stacking work with "GET" and HTTK_STK_MIME flavour of stacking. At least my limited testing shows it works. It was more complicated than I thought, primarily because our data transfer mechanism assumed that the client or the server are the ones who drive the the transmission, and in your special case it is the proxy that does so, with the intent of writing the HTTP request to the downstream proxy. All the rest is just moving code here and there to make it possible to see that it is indeed a MIME stacking.
I just noticed another minor problem: A connection takes quite long if I set forge_port to Z_PORT_EXACT or Z_PORT_GROUP. Between the "Initiating connection" and "Established connection" it takes more than 3 seconds. If I set "self.transparent_mode = TRUE" this gets really slow because then the connection seems to be not reused, if it's set to false it goes really fast (only the first request and if there was no request for some time take the 3 seconds). Why is this so? Does it wait for the other connection to somehow time out before it can use the same port? With Z_PORT_ANY it works flawlessly...
GROUP should work without problems, Z_PORT_EXACT will almost always fail, except if there is only one connection at a time. The problem is that the client-side and the server side tuples _must_ be different. For instance in connection tracking there's no way to differentiate between the two connections if the complete tuples are equal. Earlier, with the NAT based tproxies (tproxy2 and 3) this collision was a real problem (conntrack removed entries based on a timer and had no way of knowing that a given tuple was already released). Now with tproxy4, the local stack should have a chance to differentiate between the incoming and the outgoing connections, even if their tuples match completely: the local endpoint of the socket is different. client side connection: client IP:port -> server IP:port (local) server side connection: client IP:port (local) -> server IP:port But still, our 2.6.17-4.x kernels include conntrack for other purposes, so the conntrack collision still applies. However Z_PORT_GROUP should work, all it does is that it tries to allocate a port in the same port range in userspace, which means that it performs a loop on the permitted set of ports and tries to bind to each of them. I don't really see how that could take a long time. Some investigation seems to be necessary here. Probably a tcpdump could help (is the SYN sent out right at the "initiating" log message, or later, etc)
And a more cosmetical issue I found during the investigation of the connection problem: When I attach an strace to a process with a stacked program I get a huge filte (18MB) with thousands of "close(229284) = -1 EBADF (Bad file descriptor)" At one point this process then calls the stacked proxy and seems to normally continue. Is this normal? I've attached the log of this process below.
This is bug. Closing unopened file descriptors in a multi-threaded program causes unpredictable behaviour. Can you send me the complete strace? Here's the patch: M modules/http/httpfltr.c M libproxy/transfer2.c M libproxy/zorp/proxy/transfer2.h * modified files --- orig/libproxy/transfer2.c +++ mod/libproxy/transfer2.c @@ -278,7 +278,7 @@ z_transfer2_update_cond(ZTransfer2 *self { if (!z_transfer2_get_status(self, ZT2S_EOF_SOURCE)) { - if (z_transfer2_buffer_empty(&self->buffers[0])) + if (z_transfer2_buffer_empty(&self->buffers[0]) && !z_transfer2_get_status(self, ZT2S_PROXY_OUT)) z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, TRUE); else z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE), G_IO_OUT, TRUE); @@ -296,7 +296,7 @@ z_transfer2_update_cond(ZTransfer2 *self /* no stacking */ if (!z_transfer2_get_status(self, ZT2S_EOF_SOURCE)) { - if (z_transfer2_buffer_empty(&self->buffers[0]) || z_transfer2_get_status(self, ZT2S_EOF_DEST) != 0) + if ((z_transfer2_buffer_empty(&self->buffers[0]) || z_transfer2_get_status(self, ZT2S_EOF_DEST) != 0) && !z_transfer2_get_status(self, ZT2S_PROXY_OUT)) z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, TRUE); else z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_DEST), G_IO_OUT, TRUE); @@ -876,6 +876,7 @@ z_transfer2_run_method(ZTransfer2 *self) { z_enter(); z_transfer2_switch_to_transfer_context(self); + z_transfer2_update_cond(self); z_transfer2_update_status(self, ZT2S_STARTED, TRUE); z_transfer2_update_status(self, ZT2S_SUSPENDED, FALSE); --- orig/libproxy/zorp/proxy/transfer2.h +++ mod/libproxy/zorp/proxy/transfer2.h @@ -36,6 +36,9 @@ typedef enum #define ZT2S_SOFT_EOF_SOURCE 0x0400 #define ZT2S_SOFT_EOF_DEST 0x0800 +/* if this flag is set, the proxy has something to say, so regardless of the buffer contents, poll the output side */ +#define ZT2S_PROXY_OUT 0x1000 + #define ZT2S_EOF_BITS (ZT2S_EOF_SOURCE | ZT2S_EOF_DEST | ZT2S_SOFT_EOF_SOURCE | ZT2S_SOFT_EOF_DEST) #define ZT2E_STACKED 0x02 @@ -158,6 +161,15 @@ z_transfer2_set_content_format(ZTransfer self->content_format = content_format; } +static inline void +z_transfer2_set_proxy_out(ZTransfer2 *self, gboolean enable) +{ + if (enable) + self->status |= ZT2S_PROXY_OUT; + else + self->status &= ~ZT2S_PROXY_OUT; +} + static inline const gchar * z_transfer2_get_stack_info(ZTransfer2 *self) { --- orig/modules/http/httpfltr.c +++ mod/modules/http/httpfltr.c @@ -154,7 +154,10 @@ http_transfer_src_read(ZTransfer2 *s, ZS *bytes_read = move; if (self->stacked_preamble_ofs == self->stacked_preamble->len) - self->src_read_state = HTTP_SR_READ_INITIAL; + { + z_transfer2_set_proxy_out(s, FALSE); + self->src_read_state = HTTP_SR_READ_INITIAL; + } return G_IO_STATUS_NORMAL; } else @@ -727,22 +730,12 @@ http_transfer_stack_proxy(ZTransfer2 *s, /* query python for a stacked proxy */ - if (self->transfer_type != HTTP_TRANSFER_NORMAL && self->transfer_type != HTTP_TRANSFER_TO_BLOB) + if (self->suppress_data || (self->transfer_type != HTTP_TRANSFER_NORMAL && self->transfer_type != HTTP_TRANSFER_TO_BLOB)) { *stacked = NULL; return TRUE; } - /* we don't stack anything, if - 1) we suppress data - 2) data is not indicated by either the presence of some header fields nor do we expect data - */ - if (self->suppress_data || - !(self->expect_data || http_lookup_header(headers, "Transfer-Encoding", &hdr) || http_lookup_header(headers, "Content-Length", &hdr))) - { - *stacked = NULL; - return TRUE; - } z_policy_lock(self->super.owner->thread); @@ -773,17 +766,21 @@ http_transfer_stack_proxy(ZTransfer2 *s, } success = TRUE; - switch (stack_type) + if (stack_type == HTTP_STK_MIME) + self->push_mime_headers = TRUE; + + /* we don't stack anything, if + 1) we suppress data + 2) data is not indicated by either the presence of some header fields nor do we expect data + */ + if (!(self->expect_data || self->push_mime_headers || http_lookup_header(headers, "Transfer-Encoding", &hdr) || http_lookup_header(headers, "Content-Length", &hdr))) { - case HTTP_STK_NONE: - break; - case HTTP_STK_MIME: - self->push_mime_headers = TRUE; - /* fallthrough */ - case HTTP_STK_DATA: - success = z_proxy_stack_object(s->owner, stack_object, stacked); - break; + *stacked = NULL; + goto unref_unlock; } + + if (stack_type != HTTP_STK_NONE) + success = z_proxy_stack_object(s->owner, stack_object, stacked); unref_unlock: z_policy_var_unref(proxy_stack_tuple); z_policy_unlock(self->super.owner->thread); @@ -851,6 +848,7 @@ http_transfer_setup(ZTransfer2 *s) { self->format_preamble_func((HttpProxy *) self->super.owner, TRUE, self->stacked_preamble); g_string_append(self->stacked_preamble, "\r\n"); + z_transfer2_set_proxy_out(s, TRUE); } self->src_chunked = FALSE; @@ -1028,7 +1026,7 @@ http_transfer_run(ZTransfer2 *s) HttpTransfer *self = Z_CAST(s, HttpTransfer); GError *local_error = NULL; - if (self->content_length != HTTP_LENGTH_NONE && self->content_length != 0) + if ((self->content_length != HTTP_LENGTH_NONE || self->push_mime_headers) && self->content_length != 0) { return Z_SUPER(self, ZTransfer2)->run(&self->super); } -- Bazsi
Hi, Thanks for the patch, I'll try it tomorrow. Here's the strace file: http://www.2shared.com/file/3560857/288d71f7/strace.html The policy-file which was used for creating it looked like this: from Zorp.Core import * from Zorp.Http import * import os InetZone('attack', '10.1.1.0/24', inbound_services=['HTTP'], outbound_services=['HTTP']) def http(): Service(name="HTTP", proxy_class=MyHttp, router=TransparentRouter(forge_addr=TRUE, forge_port=Z_PORT_ANY)) Listener(DBIface('eth0', 50080), 'HTTP', transparent=TRUE, backlog=255, threaded=FALSE) class MyHttp(HttpProxy): def config(self): HttpProxy.config(self) self.transparent_mode = TRUE self.rerequest_attempts = 1 self.request_stack["POST"] = (HTTP_STK_MIME, (Z_STACK_PROGRAM, "/bin/cat")) @Árpád Magosányi: The patches for AnyPy look very promising to me. Especially the streams were quite problematic for me before because they always blocked (at least for me, I'm not a python pro). Also thanks a lot for the tutorial, it looks great! Thomas Wenz -- GMX startet ShortView.de. Hier findest Du Leute mit Deinen Interessen! Jetzt dabei sein: http://www.shortview.de/wasistshortview.php?mc=sv_ext_mf@gmx
Hi, The patch works fine! Is it also possible with GET to move the call of the program up? Setting self.rerequest_attempts = 1 doesn't change the behaviour like it did with POST because there are some checks against has_data which naturally fail because a GET doesn't have data. The Z_PORT_GROUP has the same effect here like Z_PORT_EXACT with the 3s waiting time. But I don't mind about just using the Z_PORT_ANY as it's not really a big difference. I think I just stumbled over a problem in the prerelease 3.3.1. I just used a plain policy file for HTTPS in 3.3.1 as well as in 3.1.4. It looks like this: class HttpsProxy(PsslProxy): def config(self): PsslProxy.config(self) self.client_need_ssl = TRUE self.client_key_file = '/home/zorp/conf/server.key' self.client_cert_file = '/home/zorp/conf/server.crt' self.client_verify_type = PSSL_VERIFY_NONE self.server_need_ssl = TRUE self.server_ca_directory = '/etc/zorp/https_trusted_ca.crt' self.server_verify_type = PSSL_VERIFY_NONE self.stack_proxy = HttpProxy However, this does not work in 3.3.1 anymore. I've uploaded the logfiles at http://pastebin.com/f11e4da70 (3.1.4) http://pastebin.com/f2a09d626 (3.3.1; really ends at this point and the browser waits for data!) It looks like in 3.3.1 the HttpProxy is stacked when the Pssl proxy is already in the process of shutting down and thus it doesn't transfer correctly. Is there a different syntax in the policy file needed? Thomas Wenz -- Psssst! Schon das coole Video vom GMX MultiMessenger gesehen? Der Eine für Alle: http://www.gmx.net/de/go/messenger03
Hi, Any idea on the SSL issue? I couldn't find a solution yet so I probably need to go back to 3.1... Thomas Wenz -- Psssst! Schon vom neuen GMX MultiMessenger gehört? Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger
On Tue, 2008-07-15 at 02:42 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Any idea on the SSL issue? I couldn't find a solution yet so I probably need to go back to 3.1...
Thomas Wenz
Sorry for the silence, I'm currently in the middle of releasing syslog-ng 3.0. I can hopefully allocate some time today or tomorrow. -- Bazsi
On Tue, 2008-07-15 at 02:42 +0200, thomas.wenz@gmx-topmail.de wrote:
Hi,
Any idea on the SSL issue? I couldn't find a solution yet so I probably need to go back to 3.1...
The attached patch fixes the SSL problem. There was some API changes in the Zorp core, which was not followed up in the GPLd pssl proxy. Also some cleanup was highly due, which I did at the same time. So please find a patch attached. It made the Pssl proxy work for me again. pssl.c | 197 ++++++++++++++++++++--------------------------------------------- 1 file changed, 62 insertions(+), 135 deletions(-) -- Bazsi
Hi, I just tried the patch and I can confirm that it works now. Thanks! Thomas Wenz -- Psssst! Schon das coole Video vom GMX MultiMessenger gesehen? Der Eine für Alle: http://www.gmx.net/de/go/messenger03
Hi, Thanks for taking you so much time to help me! Your idea with self.rerequest_attempts setting to 1 is fantastic! I tried it and it moves the stacking so much up like I want it! Now I can do exactly what I described with POST! The problem left is that stacking is still not done on GET-requests. Somehow, your suggested change didn't do anything because it's an OR-clause and if any of these checks fails it enters the section and ends. If I put a comment around this whole if-section, it's a little bit better: the program gets executed but a) it's then not executed at the same time like with POST and self.rerequest_attempts=1 so it seems like the alternative path with blobs can only be used with POST. I tried changing the has_data value in the http_fetch_request function but it didn't help (probably there's more needed). b) it's then executed but still no data (= no headers) is passed so it's quite useless. There must be another check somewhere later in the program which prevents sending the headers when there's no data part. I just noticed another minor problem: A connection takes quite long if I set forge_port to Z_PORT_EXACT or Z_PORT_GROUP. Between the "Initiating connection" and "Established connection" it takes more than 3 seconds. If I set "self.transparent_mode = TRUE" this gets really slow because then the connection seems to be not reused, if it's set to false it goes really fast (only the first request and if there was no request for some time take the 3 seconds). Why is this so? Does it wait for the other connection to somehow time out before it can use the same port? With Z_PORT_ANY it works flawlessly... And a more cosmetical issue I found during the investigation of the connection problem: When I attach an strace to a process with a stacked program I get a huge filte (18MB) with thousands of "close(229284) = -1 EBADF (Bad file descriptor)" At one point this process then calls the stacked proxy and seems to normally continue. Is this normal? I've attached the log of this process below. Thomas Wenz 7415 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb6de3bf8) = 7416 7416 dup2(19, 0 <unfinished ...> 7416 <... dup2 resumed> ) = 0 7416 dup2(21, 1 <unfinished ...> 7416 <... dup2 resumed> ) = 1 7416 dup2(23, 3 <unfinished ...> 7416 <... dup2 resumed> ) = 3 7416 getrlimit(RLIMIT_NOFILE, <unfinished ...> 7416 <... getrlimit resumed> {rlim_cur=250*1024, rlim_max=250*1024}) = 0 7416 close(4 <unfinished ...> 7416 <... close resumed> ) = 0 7416 close(5 <unfinished ...> 7416 <... close resumed> ) = 0 ... 7416 <... close resumed> ) = 0 7416 close(24 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) 7416 close(25 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) 7416 close(26 <unfinished ...> 7416 <... close resumed> ) = -1 EBADF (Bad file descriptor) .... 7416 close(255999) = -1 EBADF (Bad file descriptor) 7416 execve("/bin/sh", ["/bin/sh", "-c", "/usr/bin/cat"], [/* 15 vars */]) = 0 -- Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
participants (3)
-
Balazs Scheidler
-
thomas.wenz@gmx-topmail.de
-
Árpád Magosányi